将一个对象序列化为XML

334

我有一个C#类我已经继承了它。我已经成功地“构建”了这个对象。但是我需要将这个对象序列化为XML。有没有简单的方法可以做到这一点?

看起来这个类已经被设置为可序列化,但我不确定如何获得XML表示。我的类定义如下:

[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.domain.com/test")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://www.domain.com/test", IsNullable = false)]
public partial class MyObject
{
  ...
}

这是我认为我可以做的,但它不起作用:

MyObject o = new MyObject();
// Set o properties
string xml = o.ToString();

我如何获得该对象的XML表示?


请参阅:CodeProject上的XML序列化和反序列化 - Behzad Ebrahimi
1
我开发了一个简单的库来实现这个功能:https://github.com/aishwaryashiva/SaveXML - Aishwarya Shiva
19个回答

592

您需要使用XmlSerializer进行XML序列化。以下是一个示例代码片段。

 XmlSerializer xsSubmit = new XmlSerializer(typeof(MyObject));
 var subReq = new MyObject();
 var xml = "";

 using(var sww = new StringWriter())
 {
     using(XmlWriter writer = XmlWriter.Create(sww))
     {
         xsSubmit.Serialize(writer, subReq);
         xml = sww.ToString(); // Your XML
     }
 }

根据@kiquenet的请求,需要一个通用类:
public class MySerializer<T> where T : class
{
    public static string Serialize(T obj)
    {
        XmlSerializer xsSubmit = new XmlSerializer(typeof(T));
        using (var sww = new StringWriter())
        {
            using (XmlTextWriter writer = new XmlTextWriter(sww) { Formatting = Formatting.Indented })
            {
                xsSubmit.Serialize(writer, obj);
                return sww.ToString();
            }
        }
    }
}

使用方法:

string xmlMessage = MySerializer<MyClass>.Serialize(myObj);

10
没有使用 XmlWriter writer = XmlWriter.Create(sww); 这行代码似乎也可以完美运行。 - Paul Hunt
16
将序列化对象格式化为:XmlTextWriter writer = new XmlTextWriter(sww) { Formatting = Formatting.Indented };,而不是使用 XmlWriter writer = XmlWriter.Create(sww); - Tono Nam
4
因为XmlWriter封装了StringWriter,所以你不需要同时释放两个(第一个使用是多余的),对吗?我假设XmlWriter会处理它的释放... - talles
6
XmlWriter没有封装StringWriter,而是利用传入的StringWriter,并且没有期望或责任来处理它的释放。此外,StringWriter超出了XmlWriter的范围。当XmlWriter被释放时,您可能仍需要使用它。如果XmlWriter释放您的StringWriter,那么它的行为就不好了。一般规则是,如果您声明了需要处理的内容,那么您需要负责处理它。隐含在这个规则中的是,您没有声明的任何内容都不应该进行处理。因此,两个using语句都是必要的。 - Arkaine55
6
使用System.Xml.Serialization;、System.IO;和System.Xml;。 - timothy
显示剩余4条评论

132
我修改了我的代码,改为返回一个字符串而不是像下面那样使用 ref 变量。
public static string Serialize<T>(this T value)
{
    if (value == null)
    {
        return string.Empty;
    }
    try
    {
        var xmlserializer = new XmlSerializer(typeof(T));
        var stringWriter = new StringWriter();
        using (var writer = XmlWriter.Create(stringWriter))
        {
            xmlserializer.Serialize(writer, value);
            return stringWriter.ToString();
        }
    }
    catch (Exception ex)
    {
        throw new Exception("An error occurred", ex);
    }
}

使用方法如下:

var xmlString = obj.Serialize();

8
非常好的解决方案,我喜欢你将其实现为扩展方法的方式。 - Spyros
70
这里我建议你删除try...catch块。它没有任何好处,只会混淆被抛出的错误。 - jammycakes
8
你不也需要在StringWriter上使用using语句吗?例如:using(var stringWriter = new StringWriter()) - Steven Quick
3
不!当你在那里抛出一个新的 Exception 时,你已经通过方法 "Serialize <>" 扩展了堆栈跟踪。 - user11909
1
太棒了。这个 Serialize<T>(this T value) 部分非常有用。以前从未想过将扩展方法和泛型结合在一起。 - dotNET
显示剩余3条评论

48
以下函数可复制到任何对象中,使用 System.Xml 命名空间添加一个 XML 保存功能。
/// <summary>
/// Saves to an xml file
/// </summary>
/// <param name="FileName">File path of the new xml file</param>
public void Save(string FileName)
{
    using (var writer = new System.IO.StreamWriter(FileName))
    {
        var serializer = new XmlSerializer(this.GetType());
        serializer.Serialize(writer, this);
        writer.Flush();
    }
}

要从保存的文件中创建对象,请添加以下函数并替换 [ObjectType] 为要创建的对象类型。

/// <summary>
/// Load an object from an xml file
/// </summary>
/// <param name="FileName">Xml file name</param>
/// <returns>The object created from the xml file</returns>
public static [ObjectType] Load(string FileName)
{
    using (var stream = System.IO.File.OpenRead(FileName))
    {
        var serializer = new XmlSerializer(typeof([ObjectType]));
        return serializer.Deserialize(stream) as [ObjectType];
    }
}

using 块中使用 writer.Flush() 是多余的 - writerDispose() 方法会自动刷新它。 - bavaza
7
我的经验发现这并不正确。使用更大的数据时,using语句将在缓冲被清除之前处理流。我百分之百建议显式调用flush。 - Ben Gripka
7
writer.Flush() 不是多余的,它必须存在。没有 Flush,可能会导致部分数据仍然在 StreamWriter 缓冲区中,文件被处理并且某些数据丢失。 - Tomas Kubes
我非常喜欢你的代码:简短而整洁。我的问题在于将函数一遍又一遍地复制到不同的类中:这不是代码重复吗?其他答案建议使用带有模板扩展方法的通用库,我会接受这个建议。你觉得呢? - Michael G

36

扩展类:

using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace MyProj.Extensions
{
    public static class XmlExtension
    {
        public static string Serialize<T>(this T value)
        {
            if (value == null) return string.Empty;

            var xmlSerializer = new XmlSerializer(typeof(T));

            using (var stringWriter = new StringWriter())
            {
                using (var xmlWriter = XmlWriter.Create(stringWriter,new XmlWriterSettings{Indent = true}))
                {
                    xmlSerializer.Serialize(xmlWriter, value);
                    return stringWriter.ToString();
                }    
            }
        }
    }
}

使用方法:

Foo foo = new Foo{MyProperty="I have been serialized"};

string xml = foo.Serialize();

只需在要使用扩展方法的文件中引用包含扩展方法的命名空间即可(例如,在我的示例中,可以使用using MyProj.Extensions;)。

请注意,如果您想将扩展方法仅限于特定类(例如Foo),则可以在扩展方法中替换T参数,例如:

public static string Serialize(this Foo value){...}


31
您可以使用以下代码将任何对象序列化为XML格式。
public static bool Serialize<T>(T value, ref string serializeXml)
{
    if (value == null)
    {
        return false;
    }
    try
    {
        XmlSerializer xmlserializer = new XmlSerializer(typeof(T));
        StringWriter stringWriter = new StringWriter();
        XmlWriter writer = XmlWriter.Create(stringWriter);

        xmlserializer.Serialize(writer, value);

        serializeXml = stringWriter.ToString();

        writer.Close();
        return true;
    }
    catch (Exception ex)
    {
        return false;
    }
}

您可以从客户端调用此功能。


30

以上所有被点赞的答案都是正确的。这只是最简单的版本:

private string Serialize(Object o)
{
    using (var writer = new StringWriter())
    {
        new XmlSerializer(o.GetType()).Serialize(writer, o);
        return writer.ToString();
    }
}

21

要序列化一个对象,可以这么做:

 using (StreamWriter myWriter = new StreamWriter(path, false))
 {
     XmlSerializer mySerializer = new XmlSerializer(typeof(your_object_type));
     mySerializer.Serialize(myWriter, objectToSerialize);
 }

同时请记住,为了使XmlSerializer正常工作,您需要一个无参数的构造函数。


2
这让我抓狂了。我一直想不通为什么它总是空白的。后来意识到,在阅读了你的答案之后,我没有一个没有参数的构造函数。谢谢。 - Andy

19

我将以Ben Gripka的复制答案开始:

public void Save(string FileName)
{
    using (var writer = new System.IO.StreamWriter(FileName))
    {
        var serializer = new XmlSerializer(this.GetType());
        serializer.Serialize(writer, this);
        writer.Flush();
    }
}

我之前使用过这段代码,但实际情况表明这种解决方案有点问题。通常大多数程序员在保存时将设置序列化,加载时反序列化设置。这是一种乐观的场景。但一旦由于某些原因导致序列化失败,文件只被部分写入,XML文件就不完整且无效。结果XML反序列化不起作用,你的应用程序可能会在启动时崩溃。如果文件不太大,我建议先将对象序列化到MemoryStream,然后再将流写入文件。如果存在一些复杂的自定义序列化,则此情况尤为重要。你永远无法测试所有情况。

public void Save(string fileName)
{
    //first serialize the object to memory stream,
    //in case of exception, the original file is not corrupted
    using (MemoryStream ms = new MemoryStream())
    {
        var writer = new System.IO.StreamWriter(ms);    
        var serializer = new XmlSerializer(this.GetType());
        serializer.Serialize(writer, this);
        writer.Flush();

        //if the serialization succeed, rewrite the file.
        File.WriteAllBytes(fileName, ms.ToArray());
    }
}

在现实世界中,反序列化应该考虑到损坏的序列化文件,有时会发生这种情况。Ben Gripka提供的加载函数很好。

public static [ObjectType] Load(string fileName)
{
    using (var stream = System.IO.File.OpenRead(fileName))
    {
        var serializer = new XmlSerializer(typeof([ObjectType]));
        return serializer.Deserialize(stream) as [ObjectType];        
    }    
}

它可以被某些恢复场景所包裹。它适用于设置文件或其他可能在出现问题时被删除的文件。

public static [ObjectType] LoadWithRecovery(string fileName)
{
    try
    {
        return Load(fileName);
    }
    catch(Excetion)
    {
        File.Delete(fileName); //delete corrupted settings file
        return GetFactorySettings();
    }
}

在将MemoryStream写入文件时,例如由于停电等原因,进程不可能被中断吗? - John Smith
1
是的,这是可能的。您可以通过将设置写入临时文件,然后替换原始文件来避免这种情况。 - Tomas Kubes

9

这比调用类的ToString方法稍微复杂一些,但并不多。

下面是一个简单易用的函数,可将任何类型的对象进行序列化。它返回包含序列化XML内容的字符串:

public string SerializeObject(object obj)
{
    System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
    System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
    using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) {
        serializer.Serialize(ms, obj);
        ms.Position = 0;
        xmlDoc.Load(ms);
        return xmlDoc.InnerXml;
    }
}

9

在上述解决方案的基础上,这里提供了一个扩展类,您可以使用它来序列化和反序列化任何对象。其他任何 XML 属性由您自定义。

使用方法如下:

        string s = new MyObject().Serialize(); // to serialize into a string
        MyObject b = s.Deserialize<MyObject>();// deserialize from a string



internal static class Extensions
{
    public static T Deserialize<T>(this string value)
    {
        var xmlSerializer = new XmlSerializer(typeof(T));

        return (T)xmlSerializer.Deserialize(new StringReader(value));
    }

    public static string Serialize<T>(this T value)
    {
        if (value == null)
            return string.Empty;

        var xmlSerializer = new XmlSerializer(typeof(T));

        using (var stringWriter = new StringWriter())
        {
            using (var xmlWriter = XmlWriter.Create(stringWriter, new XmlWriterSettings { Indent = true }))
            {
                xmlSerializer.Serialize(xmlWriter, value);
                return stringWriter.ToString();
            }
        }
    }
}

感谢您展示了使用方法: 当字符串为 "" 时,反序列化应该返回什么? - Meryan
我想我想出了解决方案,我是一个复制粘贴的程序员 :) // https://dev59.com/2Gw15IYBdhLWcg3wkMnz public static T Deserialize<T>(this string value) where T : new() { var xmlSerializer = new XmlSerializer(typeof(T)); if (value == "") return new T(); return (T)xmlSerializer.Deserialize(new StringReader(value)); } - Meryan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接