boxmoe_header_banner_img

Hello! 欢迎来到悠悠畅享网!

文章导读

C#的XmlSerializer如何序列化对象为XML?


avatar
作者 2025年8月22日 18

c#中序列化对象xml最直接方式是使用xmlserializer类;2. 核心步骤为创建xmlserializer实例、调用serialize方法写入流;3. 处理复杂类型需注意嵌套对象自动递归、集合默认带包装元素,可用[xmlarray]或[xmlelement]定制;4. 自定义xml结构可用[xmlelement]改元素名、[xmlattribute]变属性、[xmlignore]忽略成员、[xmlinclude]支持多态;5. 常见问题包括必须提供公共无参构造函数、只读属性反序列化失败、循环引用导致溢出、首次序列化性能低、命名空间需手动控制;6. 对比其他序列化方式:xmlserializer适合严格xml schema场景,datacontractserializer适合.net内部数据契约,json序列化库适合现代web api和跨平台交互。

C#的XmlSerializer如何序列化对象为XML?

c#中,要将一个对象序列化成XML,最直接且常用的方式就是利用

System.Xml.Serialization

命名空间下的

XmlSerializer

类。它能将对象的公共属性和字段转换为XML元素和属性,反之亦然,实现对象与XML文档之间的双向转换。

解决方案

使用

XmlSerializer

将C#对象序列化为XML,核心步骤是创建一个

XmlSerializer

实例,指定要序列化的对象类型,然后调用其

Serialize

方法,将对象写入到一个流(如

MemoryStream

)或

TextWriter

中。

以下是一个简单的示例,展示如何将一个

Book

对象序列化为XML字符串

using System; using System.IO; using System.Xml.Serialization;  public class Book {     public String Title { get; set; }     public string Author { get; set; }     public int PublicationYear { get; set; }     // 默认构造函数是XmlSerializer序列化和反序列化所必需的     public Book() { }      public Book(string title, string author, int year)     {         Title = title;         Author = author;         PublicationYear = year;     } }  public class XmlSerializationExample {     public static void Main(string[] args)     {         // 创建一个要序列化的对象实例         Book myBook = new Book("The Hitchhiker's Guide to the Galaxy", "Douglas Adams", 1979);          // 创建XmlSerializer实例,指定要序列化的类型         XmlSerializer serializer = new XmlSerializer(typeof(Book));          // 使用StringWriter来捕获XML输出到字符串         using (StringWriter writer = new StringWriter())         {             // 执行序列化             serializer.Serialize(writer, myBook);              // 获取序列化后的XML字符串             string xmlString = writer.ToString();             Console.WriteLine("Serialized XML:");             Console.WriteLine(xmlString);         }          // 也可以序列化到文件         string filePath = "myBook.xml";         using (FileStream fs = new FileStream(filePath, FileMode.Create))         {             serializer.Serialize(fs, myBook);             Console.WriteLine($"nObject serialized to {filePath}");         }          // 反序列化示例 (从文件读取)         Console.WriteLine("nDeserializing from file...");         using (FileStream fsRead = new FileStream(filePath, FileMode.Open))         {             Book deserializedBook = (Book)serializer.Deserialize(fsRead);             Console.WriteLine($"Deserialized Book: {deserializedBook.Title} by {deserializedBook.Author} ({deserializedBook.PublicationYear})");         }     } }

运行这段代码,你会看到

Book

对象被转换成了一个结构清晰的XML,其中

Title

Author

PublicationYear

都成了XML元素。这感觉就像是把一个C#对象“拍扁”成了文本格式,方便存储和传输。

XmlSerializer在处理复杂类型或集合时有哪些注意事项?

当你的数据模型变得复杂,比如包含嵌套对象、列表或数组时,

XmlSerializer

的处理方式就显得尤为重要,而且它也提供了一些特性(Attributes)来精细控制XML的输出结构。

首先,对于嵌套对象

XmlSerializer

默认会递归地序列化它们。比如,如果你的

Book

类里有一个

Publisher

对象,那么

Publisher

的公共属性也会被序列化为XML元素,嵌套在

<Book>

元素内部。这很符合直觉,但如果嵌套层级很深,生成的XML可能会变得很冗长。

处理集合类型(如

List<T>

T[]

IEnumerable<T>

)时,

XmlSerializer

会默认生成一个包装元素,其名称通常是“ArrayOf”加上集合元素的类型名。比如

List<Book>

会序列化成

<Book>...<Book>...

。如果你觉得这个默认的包装元素名不够语义化,或者根本不想要这个包装,可以使用

[XmlArray("Books")]

[XmlElement("Book")]

来定制。我个人觉得,直接使用

[XmlElement("Book")]

List<Book>

属性上,可以避免

ArrayOf

前缀,让XML看起来更简洁,更符合某些API的要求。

更进一步的自定义XML结构,这才是

XmlSerializer

的真正魅力所在:

  • [XmlElement("NewElementName")]

    : 改变属性对应的XML元素名。比如,

    [XmlElement("BookTitle")] public string Title { get; set; }

    会让

    Title

    属性序列化为

    <BookTitle>

    而不是

    <Title>

  • [XmlAttribute("Year")]

    : 将属性序列化为XML元素的属性而非子元素。

    [XmlAttribute("Year")] public int PublicationYear { get; set; }

    会让

    PublicationYear

    变成

    <Book Year="1979">

    。这在XML中非常常见,比如用于ID或版本号。

  • [XmlIgnore]

    : 忽略某个公共属性或字段,不将其序列化到XML中。如果你的类里有一些内部状态或只读属性,不希望暴露在XML里,这个特性就很有用。

  • [XmlArray("Items")]

    [XmlArrayItem("Item")]

    : 精确控制集合的包装元素和内部元素的名称。例如,

    [XmlArray("Chapters"), XmlArrayItem("Chapter")] public List<string> Chapters { get; set; }

    会生成

    <Chapters><Chapter>...</Chapter></Chapters>

  • [XmlInclude(typeof(DerivedType))]

    : 这是处理多态性的关键。如果你的类包含一个基类类型的属性,但运行时它实际上是派生类的实例,

    XmlSerializer

    默认可能无法正确序列化派生类的特有成员。通过在基类或包含属性的类上添加

    [XmlInclude(typeof(DerivedType))]

    ,你可以告诉

    XmlSerializer

    在序列化时,这个属性可能包含

    DerivedType

    的实例,从而正确处理其成员。这对于构建灵活的数据模型非常重要。

这些特性提供了非常强大的控制力,让你能够将C#对象映射到几乎任何复杂的XML Schema。我通常会根据预期的XML输出结构,灵活运用这些特性,而不是完全依赖

XmlSerializer

的默认行为。

使用XmlSerializer时常见的序列化问题及如何解决?

虽然

XmlSerializer

功能强大,但在实际使用中,确实会遇到一些让人头疼的问题。我个人就没少在这上面踩过坑。

一个最常见、也最让人困惑的问题是:被序列化的类必须有一个公共的无参构造函数。哪怕你的类有其他带参数的构造函数,

XmlSerializer

在反序列化时,仍然会尝试调用这个默认的无参构造函数来创建对象实例。如果缺少,它就会抛出异常,通常是

InvalidOperationException

。解决方案很简单,就是给你的类加一个

public YourClass() { }

。这听起来有点反直觉,毕竟很多时候我们习惯用带参数的构造函数来初始化对象,但在序列化/反序列化场景下,这是个铁律。

其次,只读属性和私有字段默认是不会被

XmlSerializer

序列化的。

XmlSerializer

只关心公共的、可读写的属性和公共字段。如果你想序列化私有数据,你需要通过公共属性来暴露它,或者考虑使用

DataContractSerializer

,它对私有成员有更好的支持。对于只读属性(只有

get

没有

set

),

XmlSerializer

可以序列化它,但反序列化时无法设置其值,这可能导致数据丢失

循环引用是另一个大问题。如果你的对象图存在循环引用(例如,

Person

有一个

Car

属性,而

Car

又有一个

Owner

属性指向同一个

Person

),

XmlSerializer

无法处理这种情况,它会陷入无限循环,最终导致

StackoverflowException

。这是

XmlSerializer

的一个设计限制。在设计数据模型时,要特别注意避免这种显式的循环引用,或者在序列化前手动断开这些引用,或者使用

[XmlIgnore]

来忽略那些可能导致循环的属性。

性能问题也是一个需要考虑的点。

XmlSerializer

在首次对特定类型进行序列化或反序列化时,会动态生成一个临时的序列化程序集。这个过程可能会比较耗时,导致第一次操作感觉有点慢。对于生产环境或需要高性能的场景,你可以使用

Sgen.exe

工具来预先生成这个序列化程序集,避免运行时的开销。

最后,命名空间问题有时也会让人困扰。默认情况下,

XmlSerializer

会生成一些默认的XML命名空间。如果你需要精确控制XML元素的命名空间,可以使用

XmlSerializerNamespaces

类来定义和传递命名空间前缀,或者在类和属性上使用

[XmlRoot]

[XmlElement]

[XmlAttribute]

等特性来指定

Namespace

属性。这在与外部系统集成,需要遵循严格XML Schema时尤为重要。

处理这些问题,关键在于理解

XmlSerializer

的工作原理和限制。很多时候,解决方案就是遵守它的“规矩”,或者在数据模型设计阶段就规避掉这些潜在的问题。

XmlSerializer与DataContractSerializer、Json.NET等其他序列化方式的对比与选择?

在.NET生态系统中,除了

XmlSerializer

,我们还有

DataContractSerializer

和用于JSON序列化的

Newtonsoft.Json

(或

System.Text.Json

)。它们各有侧重,选择哪一个取决于你的具体需求和应用场景。

XmlSerializer

  • 优点:对XML结构有极致的控制力,可以通过各种特性(
    [XmlElement]

    ,

    [XmlAttribute]

    ,

    [XmlArray]

    ,

    [XmlArrayItem]

    等)精确映射C#对象到复杂的XML Schema。它与SOAP web services(ASMX)紧密集成,是处理传统XML格式的首选。

  • 缺点:要求被序列化的类有公共无参构造函数;不擅长处理循环引用;默认只序列化公共属性和字段;首次使用性能开销较大。我个人觉得它在处理复杂XML结构时虽然强大,但也意味着配置起来可能比较繁琐。
  • 适用场景:与遗留系统交互,特别是基于SOAP的Web服务;需要严格遵循特定XML Schema的场景;对XML输出格式有精细控制需求时。

DataContractSerializer

  • 优点:更加面向数据契约,通过
    [DataContract]

    [DataMember]

    特性明确指定要序列化的成员,可以序列化私有成员;对版本控制有更好的支持(

    Order

    属性);性能通常比

    XmlSerializer

    好;不要求公共无参构造函数;能更好地处理循环引用(通过对象引用)。它更像是.NET内部对象序列化的通用解决方案。

  • 缺点:对生成的XML结构控制力不如
    XmlSerializer

    ,生成的XML通常比较冗余,带有默认的命名空间前缀;不适合需要精确匹配外部XML Schema的场景。

  • 适用场景:WCF服务;内部应用程序之间的数据交换;当XML结构不是首要考虑,而更关注数据本身的传输和版本兼容性时。

Newtonsoft.Json

(Json.NET) /

System.Text.Json

  • 优点:这些是用于JSON序列化的库,与XML无关。JSON作为一种轻量级的数据交换格式,在现代Web应用、restful API和移动开发中占据主导地位。JSON序列化器通常性能优异,生成的数据体积小,易于跨平台解析。
    Newtonsoft.Json

    功能强大、灵活,而

    System.Text.Json

    是.NET Core/.NET 5+内置的高性能选项。

  • 缺点:无法生成XML。
  • 适用场景:构建RESTful API;前后端分离的Web应用;移动应用数据传输;任何需要轻量级、高性能、跨平台数据交换的场景。

如何选择?

我通常会这么考虑:

  • 如果你的需求是与旧的SOAP服务交互,或者必须生成/解析特定、复杂的XML文件(比如遵循某个行业标准XML Schema),那么
    XmlSerializer

    几乎是唯一的选择,尽管它可能需要你投入更多精力去配置和调试。

  • 如果是在.NET应用程序内部,或者WCF服务之间进行数据传输,并且对XML的具体结构没有那么严格的要求,更注重数据契约和性能,那么
    DataContractSerializer

    会是更优的选择,它更“现代”一些,用起来也更省心。

  • 如果你的项目是面向Web API、前后端分离,或者需要与非.NET平台进行数据交互,并且数据格式是JSON,那么毫无疑问,选择
    Newtonsoft.Json

    System.Text.Json

    。它们是现代应用数据交换的主流。

简单来说,

XmlSerializer

是XML结构控制的专家,

DataContractSerializer

是.NET内部数据契约的专家,而JSON序列化库则是Web和跨平台数据交换的专家。根据你的“输出”格式要求和“通信”对象,选择最合适的工具,这才是最高效的做法。



评论(已关闭)

评论已关闭