【问题标题】:C# - How to xml deserialize object itself?C# - 如何 xml 反序列化对象本身?
【发布时间】:2009-07-04 02:05:55
【问题描述】:
public class Options
    {
        public FolderOption FolderOption { set; get; }

        public Options()
        {
            FolderOption = new FolderOption();
        }


        public void Save()
        {
            XmlSerializer serializer = new XmlSerializer(typeof(Options));
            TextWriter textWriter = new StreamWriter(@"C:\Options.xml");
            serializer.Serialize(textWriter, this);
            textWriter.Close();
        }

        public void Read()
        {
            XmlSerializer deserializer = new XmlSerializer(typeof(Options));
            TextReader textReader = new StreamReader(@"C:\Options.xml");
            //this = (Options)deserializer.Deserialize(textReader);
            textReader.Close();

        }
    }
}

我成功地保存了,FolderOption 的所有成员都被反序列化了。但问题是如何读回来?该行 - //this = (Options)deserializer.Deserialize(textReader);不会工作。

编辑:这个问题有什么解决办法吗?我们可以在不分配这个的情况下达到同样的目的吗?那就是将选项对象反序列化回选项。我懒得按属性做。在最高级别上执行将节省大量精力。

【问题讨论】:

    标签: c# serialization


    【解决方案1】:

    将您的 .Read() 方法构建为返回读取对象的静态函数:

    public static Options Read(string path)
    {
        XmlSerializer deserializer = new XmlSerializer(typeof(Options));
        using (TextReader textReader = new StreamReader(path))
        {
            return (Options)deserializer.Deserialize(textReader);
        }
    }
    

    然后更改您的调用代码,而不是这样:

    Options myOptions = new Options();
    myOptions.Read(@"C:\Options.xml");
    

    你做这样的事情:

    Options myOptions = Options.Read(@"C:\Options.xml");
    

    很好的区别在于,不可能有一个没有一些数据的 Options 对象。

    【讨论】:

    • 我同意,这个 IMO 是回答问题的最干净的解决方案。
    • 我不能为其他人发表评论,但这最适合我目前的项目......非常整洁。
    【解决方案2】:

    如果您的 Options 类型是结构,这将起作用,因为您可以更改结构本身。

    如果 Options 是一个类(引用类型),则不能将其分配给该实例中的引用类型的当前实例。建议您编写一个辅助类,并将您的 Read 和 Save 方法放在那里,像这样

         public class XmlSerializerHelper<T>
        {
            public Type _type;
    
            public XmlSerializerHelper()
            {
                _type = typeof(T);
            }
    
    
            public void Save(string path, object obj)
            {
                using (TextWriter textWriter = new StreamWriter(path))
                {
                    XmlSerializer serializer = new XmlSerializer(_type);
                    serializer.Serialize(textWriter, obj);
                }
    
            }
    
            public T Read(string path)
            {
                T result;
                using (TextReader textReader = new StreamReader(path))
                {
                    XmlSerializer deserializer = new XmlSerializer(_type);
                    result = (T)deserializer.Deserialize(textReader);
                }
                return result;
    
            }
        }
    

    然后从调用者那里使用它来读取和保存对象,而不是从类中尝试它。

    //In the caller
    
    var helper=new XmlSerializerHelper<Options>();
    var obj=new Options();
    
    //Write and read
    helper.Save("yourpath",obj);
    obj=helper.Read("yourpath");
    

    并将 XmlSerializerHelper 放在 Util 的命名空间中,它是可重用的,并且适用于任何类型。

    【讨论】:

    • -1 表示未实现“使用”块,也表示未使用泛型。
    • 使用 (XmlSerializer deserializer = new XmlSerializer(_type)) 不起作用。 XmlSerializer 没有实现 IDisposable。正确的应该就像约翰的回答一样,将 XmlSerializer 放在 using 块之外。
    • 糟糕,暂时忘记了 XmlSerializer 没有实现 IDisposable,已更正:)
    • 感谢您的更新。至少现在我知道 John 所说的“不使用泛型”是什么意思。
    • 使用泛型的全部意义在于避免强制转换。 // 在辅助类中 public void Save( string path, T @object );公共 T 读取(字符串路径);返回(T)结果; // 在调用者类中 obj = helper.Read( "yourpath" ); // 不需要强制转换
    【解决方案3】:

    根据定义,对象不能反序列化自身:它已经存在,反序列化会创建该类型的新实例。

    有时创建一个新的空类实例,然后用从 XML 引入的信息填充它是有意义的。该实例也可能“几乎为空”。例如,您可能会这样做,以加载用户首选项,或者一般而言,将实例设置回它以前的方式。实例的“空”或“接近空”状态将是类的有效状态:它只是不知道它在被持久化之前曾经处于什么状态。


    另外,我建议你养成实现“使用”块的习惯:

    public void Save()
    {
        XmlSerializer serializer = new XmlSerializer(typeof(Options));
        using (TextWriter textWriter = new StreamWriter(@"C:\Options.xml"))
        {
            serializer.Serialize(textWriter, this);
            // no longer needed: textWriter.Close();
        }
    }
    
    public void Read()
    {
        XmlSerializer deserializer = new XmlSerializer(typeof(Options));
        using (TextReader textReader = new StreamReader(@"C:\Options.xml"))
        {
            // no longer needed: textReader.Close();
        }
    }
    

    这将确保 TextReader 被处理掉,即使抛出异常也是如此。这就是不再需要关闭调用的原因。

    【讨论】:

      【解决方案4】:

      我认为序列化和反序列化对象的最简单方法是使用具有以下两种方法的静态类。我们还需要一个名为 StringWriterWithEncoding 的类来设置 XML 字符串的 Encoding,因为标准 StringWriter 类的 Encoding 属性是只读的。 (在这里找到:http://devproj20.blogspot.com/2008/02/writing-xml-with-utf-8-encoding-using.html

      public static class GenericXmlSerializer
      {
          public static string Serialize<T>(T obj, Encoding encoding)
          {
              XmlSerializer serializer = new XmlSerializer(typeof(T));            
              TextWriter textWriter = new StringWriterWithEncoding(new StringBuilder(), encoding);
              serializer.Serialize(textWriter, obj);
      
              return textWriter.ToString();
          }
      
          public static T Deserialize<T>(string xml)
          {
              XmlSerializer serializer = new XmlSerializer(typeof(T));
              TextReader textReader = new StringReader(xml);
              return (T)serializer.Deserialize(textReader);
          }
      }
      
      public class StringWriterWithEncoding : StringWriter
      {
          Encoding encoding;
      
          public StringWriterWithEncoding(StringBuilder builder, Encoding encoding)
              : base(builder)
          {
              this.encoding = encoding;
          }
      
          public override Encoding Encoding
          {
              get { return encoding; }
          }
      }
      

      用法:

      //serialize
      MyClass myClass = new MyClass();
      string xml = GenericXmlSerializer.Serialize<MyClass>(myClass, Encoding.Unicode);
      
      //deserialize
      MyClass myClass2 = GenericXmlSerializer.Deserialize<MyClass>(xml);
      

      【讨论】:

        【解决方案5】:

        我是扩展方法的粉丝,因此我总是使用它:

        using System.IO;
        using System.Xml.Serialization;
        
        public static class SerializationExtensionMethods
        {
            /// <summary>
            /// Serializes the object.
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="toSerialize">To serialize.</param>
            /// <returns></returns>
            public static string SerializeObjectToXml<T>(this T toSerialize)
            {
                XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());
                StringWriter textWriter = new StringWriter();
        
                xmlSerializer.Serialize(textWriter, toSerialize);
                return textWriter.ToString();
            }
        
            /// <summary>
            /// Serializes the object.
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="toSerialize">To serialize.</param>
            /// <param name="path">The path.</param>
            public static void SerializeObjectToFile<T>(this T toSerialize, string path)
            {
                string xml = SerializeObjectToXml<T>(toSerialize);
        
                using (StreamWriter sw = new StreamWriter(path, false))
                {
                    sw.Write(xml);
                }
            }
        
            /// <summary>
            /// Deserializes the specified XML.
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="xml">The XML.</param>
            /// <returns></returns>
            public static T DeserializeFromXml<T>(this T original, string xml)
            {
                XmlSerializer serializer = new XmlSerializer(typeof(T));
                TextReader textReader = new StringReader(xml);
                return (T)serializer.Deserialize(textReader);
            }
        
            /// <summary>
            /// Deserializes the specified object.
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="original">The original.</param>
            /// <param name="path">The path.</param>
            /// <returns></returns>
            public static T DeserializeFromFile<T>(this T original, string path)
            {
                string xml = string.Empty;
        
                using (StreamReader sr = new StreamReader(path))
                {
                    xml = sr.ReadToEnd();
                }
        
                return DeserializeFromXml<T>(original, xml);
            }
        }
        

        用于序列化:

        YourClassType obj = new YourClassType();
        

        List<YourClassType> obj = new List<YourClassType>();
        
        string xml = obj.SerializeObjectToXml();
        

        obj.SerializeObjectToFile("PathToYourFile"); // It will save a file with your classes serialized (works with everything with the [Serializable] attribute).
        

        用于反序列化:

        YourClassType obj = new YourClassType().DeserializeFromXml("XML string here");
        List<YourClassType> obj = new List<YourClassType>().DeserializeFromFile("XML string here");
        

        YourClassType obj = new YourClassType().DeserializeFromFile("PathToYourFile");
        

        你让它运行了:)

        我更喜欢扩展方法,因为它可以让你的代码非常干净,这适用于你拥有的每一种对象类型,只要它实现了 [Serializable] 属性。

        如果您需要指定序列化的方式(作为节点或属性),您可以在每个属性上添加属性,例如:

        [XmlElement("NameOfTheElementYouWant")] 
        [XmlAttribute("NameOfTheAttributeYouWant")]
        [XmlText]
        

        希望这对将来的某人有所帮助。

        亚历杭德罗

        【讨论】:

          【解决方案6】:

          我选择了这种方法(在 vb 中)

              Public Class SerialisableClass
          
              Public Sub SaveToXML(ByVal outputFilename As String)
          
                  Dim xmls = New System.Xml.Serialization.XmlSerializer(Me.GetType)
                  Using sw = New IO.StreamWriter(outputFilename)
                      xmls.Serialize(sw, Me)
                  End Using
          
              End Sub
          
              Private tempState As Object = Me
              Public Sub ReadFromXML(ByVal inputFilename As String)
          
                  Dim xmls = New System.Xml.Serialization.XmlSerializer(Me.GetType)
          
                  Using sr As New IO.StreamReader(inputFilename)
                      tempState = xmls.Deserialize(sr)
                  End Using
          
                  For Each pi In tempState.GetType.GetProperties()
          
                      Dim name = pi.Name
          
                      Dim realProp = (From p In Me.GetType.GetProperties
                                      Where p.Name = name And p.MemberType = Reflection.MemberTypes.Property).Take(1)(0)
          
                      realProp.SetValue(Me, pi.GetValue(tempState, Nothing), Nothing)
          
                  Next
          
              End Sub
          
          End Class
          

          然后我可以简单地使用这样的东西:

          Public Class ClientSettings
          
              Inherits SerialisableClass
          
              Public Property ZipExePath As String
              Public Property DownloadPath As String
              Public Property UpdateInstallPath As String
          
          End Class
          

          然后这样称呼它:

          Dim cs As New ClientSettings
          cs.ReadFromXML("c:\myXMLfile.xml")
          

          甚至更好(如果我添加必要的构造函数):

          Dim cs as New ClientSettings("c:\myXMLFile.xml")
          

          对我来说它看起来很干净,并且在我的情况下效果很好。

          干杯

          【讨论】:

            【解决方案7】:

            参见XmlSerializer.Deserialize Method:您可以创建如下静态方法:

                public static Options DeserializeFromFile(string filename) {    
                   // Create an instance of the XmlSerializer specifying type and namespace.
                   XmlSerializer serializer = new XmlSerializer(typeof(Options));
            
                   // A FileStream is needed to read the XML document.
                   using (FileStream fs = new FileStream(filename, FileMode.Open)) {
                       XmlReader reader = new XmlTextReader(fs);
                       return (Options) serializer.Deserialize(reader);
                   } // using
                }
            

            上面可以这样称呼:

             Options foo = Options.DeserializeFromFile(@"C:\Options.xml");
            

            【讨论】:

            • 1) 请仔细阅读他的问题。 2) -1 表示不使用“使用”块。
            • 您仍然需要使用 XmlReader。
            猜你喜欢
            • 2018-10-11
            • 2023-03-29
            • 1970-01-01
            • 2014-11-30
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多