C# - 如何xml反序列化对象本身?

时间:2022-07-30 16:58:13
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();

        }
    }
}

I managed to Save without problem, all members of FolderOption are deserialized. But the problem is how to read it back? The line - //this = (Options)deserializer.Deserialize(textReader); won't work.

我设法保存没有问题,FolderOption的所有成员都被反序列化。但问题是如何阅读它?行 - // this =(Options)deserializer.Deserialize(textReader);不行。

Edit: Any solution to this problem? Can we achieve the same purpose without assigning to this? That is deserialize Options object back into Option. I am lazy to do it property by property. Performing on the highest level would save of lot of effort.

编辑:这个问题的任何解决方案?我们可以达到同样的目的而不分配给它吗?这是将Options对象反序列化为Option。我懒得去做物业。在*别上执行将节省大量精力。

7 个解决方案

#1


This will work if your Options type is a struct, as you can a alter a struct itself.

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

If Options is a class (reference type), you can't assign to the current instance of a reference type with in that instance. Suggesting you to write a helper class, and put your Read and Save methods there, like this

如果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;

        }
    }

And then consume it from your caller, to read and save objects, instead of trying it from the class.

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

//In the caller

var helper=new XmlSerializerHelper<Options>();
var obj=new Options();

//Write and read
helper.Save("yourpath",obj);
obj=helper.Read("yourpath");

And put the XmlSerializerHelper in your Util's namespace, it is reusable and will work with any type.

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

#2


Build your .Read() method as a static function that returns the read object:

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

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

Then change your calling code so rather than something like this:

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

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

You do something like this:

你做这样的事情:

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

The nice difference is that it's impossible to ever have an Options object that doesn't have some data behind it.

不同的是,不可能有一个没有一些数据的Options对象。

#3


An object cannot deserialize itself, by definition: it already exists, and deserialization creates a new instance of the type.

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

It sometimes makes sense to create a new, empty instance of a class, then fill it in with information brought in from XML. The instance could also be "almost empty". You might do this, for instance, in order to load user preferences, or in general, to set the instance back up to the way it used to be. The "empty" or "near empty" state of the instance would be a valid state for the class: it just wouldn't know what state it used to be in before it was persisted.

创建类的新的空实例有时是有意义的,然后用XML引入的信息填充它。该实例也可能“几乎是空的”。例如,您可以执行此操作以加载用户首选项,或者通常,将实例重新设置为以前的方式。实例的“空”或“近空”状态将是该类的有效状态:它只是不知道它在持久化之前曾处于什么状态。


Also, I recommend you get into the habit of implementing "using" blocks:

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

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();
    }
}

This will ensure that the TextReaders are disposed of even if an exception is thrown. That's why the Close calls are no longer needed.

这将确保即使抛出异常也会处理TextReaders。这就是不再需要Close调用的原因。

#4


I think the simpliest way to serialize and deserialize an object is to use a static class with the following two methods. We need also a class named StringWriterWithEncoding to set the Encoding of the XML string, because the Encoding property of the standard StringWriter class is readonly. (found here: http://devproj20.blogspot.com/2008/02/writing-xml-with-utf-8-encoding-using.html)

我认为序列化和反序列化对象的最简单方法是使用具有以下两种方法的静态类。我们还需要一个名为StringWriterWithEncoding的类来设置XML字符串的编码,因为标准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; }
    }
}

Usage:

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

//deserialize
MyClass myClass2 = GenericXmlSerializer.Deserialize<MyClass>(xml);

#5


I am fan of extension methods, therefore I use this always:

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

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);
    }
}

Usage to serialize:

用于序列化:

YourClassType obj = new YourClassType();

or

List<YourClassType> obj = new List<YourClassType>();

string xml = obj.SerializeObjectToXml();

or

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

Usage to deserialize:

用于反序列化:

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

or

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

And you have it running :)

你运行它:)

I prefer extension methods because it allows you to have your code very clean, this works with every kind of object type you have, as far as it implements the [Serializable] attribute on it.

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

If you need to specify how it will be serialized (as nodes or attributes), you can add the attribute on each of your properties such as:

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

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

Hope this helps someone in the future.

希望这可以帮助将来的某个人。

Alejandro

#6


I went for this approach (in vb)

我采用这种方法(在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

I can then simply use something like this:

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

Public Class ClientSettings

    Inherits SerialisableClass

    Public Property ZipExePath As String
    Public Property DownloadPath As String
    Public Property UpdateInstallPath As String

End Class

and call it like this:

并称之为:

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

or even better (if I add the necessary constructor):

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

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

It seems nice and clean to me and works well in my situation.

这对我来说似乎很好,很干净,在我的情况下运作良好。

Cheers

#7


See XmlSerializer.Deserialize Method: You could create a static method like the following:

请参阅XmlSerializer.Deserialize方法:您可以创建如下所示的静态方法:

    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
    }

The above can be called as:

以上可称为:

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

#1


This will work if your Options type is a struct, as you can a alter a struct itself.

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

If Options is a class (reference type), you can't assign to the current instance of a reference type with in that instance. Suggesting you to write a helper class, and put your Read and Save methods there, like this

如果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;

        }
    }

And then consume it from your caller, to read and save objects, instead of trying it from the class.

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

//In the caller

var helper=new XmlSerializerHelper<Options>();
var obj=new Options();

//Write and read
helper.Save("yourpath",obj);
obj=helper.Read("yourpath");

And put the XmlSerializerHelper in your Util's namespace, it is reusable and will work with any type.

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

#2


Build your .Read() method as a static function that returns the read object:

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

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

Then change your calling code so rather than something like this:

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

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

You do something like this:

你做这样的事情:

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

The nice difference is that it's impossible to ever have an Options object that doesn't have some data behind it.

不同的是,不可能有一个没有一些数据的Options对象。

#3


An object cannot deserialize itself, by definition: it already exists, and deserialization creates a new instance of the type.

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

It sometimes makes sense to create a new, empty instance of a class, then fill it in with information brought in from XML. The instance could also be "almost empty". You might do this, for instance, in order to load user preferences, or in general, to set the instance back up to the way it used to be. The "empty" or "near empty" state of the instance would be a valid state for the class: it just wouldn't know what state it used to be in before it was persisted.

创建类的新的空实例有时是有意义的,然后用XML引入的信息填充它。该实例也可能“几乎是空的”。例如,您可以执行此操作以加载用户首选项,或者通常,将实例重新设置为以前的方式。实例的“空”或“近空”状态将是该类的有效状态:它只是不知道它在持久化之前曾处于什么状态。


Also, I recommend you get into the habit of implementing "using" blocks:

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

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();
    }
}

This will ensure that the TextReaders are disposed of even if an exception is thrown. That's why the Close calls are no longer needed.

这将确保即使抛出异常也会处理TextReaders。这就是不再需要Close调用的原因。

#4


I think the simpliest way to serialize and deserialize an object is to use a static class with the following two methods. We need also a class named StringWriterWithEncoding to set the Encoding of the XML string, because the Encoding property of the standard StringWriter class is readonly. (found here: http://devproj20.blogspot.com/2008/02/writing-xml-with-utf-8-encoding-using.html)

我认为序列化和反序列化对象的最简单方法是使用具有以下两种方法的静态类。我们还需要一个名为StringWriterWithEncoding的类来设置XML字符串的编码,因为标准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; }
    }
}

Usage:

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

//deserialize
MyClass myClass2 = GenericXmlSerializer.Deserialize<MyClass>(xml);

#5


I am fan of extension methods, therefore I use this always:

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

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);
    }
}

Usage to serialize:

用于序列化:

YourClassType obj = new YourClassType();

or

List<YourClassType> obj = new List<YourClassType>();

string xml = obj.SerializeObjectToXml();

or

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

Usage to deserialize:

用于反序列化:

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

or

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

And you have it running :)

你运行它:)

I prefer extension methods because it allows you to have your code very clean, this works with every kind of object type you have, as far as it implements the [Serializable] attribute on it.

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

If you need to specify how it will be serialized (as nodes or attributes), you can add the attribute on each of your properties such as:

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

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

Hope this helps someone in the future.

希望这可以帮助将来的某个人。

Alejandro

#6


I went for this approach (in vb)

我采用这种方法(在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

I can then simply use something like this:

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

Public Class ClientSettings

    Inherits SerialisableClass

    Public Property ZipExePath As String
    Public Property DownloadPath As String
    Public Property UpdateInstallPath As String

End Class

and call it like this:

并称之为:

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

or even better (if I add the necessary constructor):

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

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

It seems nice and clean to me and works well in my situation.

这对我来说似乎很好,很干净,在我的情况下运作良好。

Cheers

#7


See XmlSerializer.Deserialize Method: You could create a static method like the following:

请参阅XmlSerializer.Deserialize方法:您可以创建如下所示的静态方法:

    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
    }

The above can be called as:

以上可称为:

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