1. 项目概述:为什么在 .NET 中还要死磕 XML 序列化?
在 .NET 生态里,JSON 已经成了事实上的数据交换默认格式, System.Text.Json 高性能、轻量、开箱即用,连 ASP.NET Core 默认都用它做 API 响应序列化。但如果你正在维护一个运行了十年以上的金融清算系统、某省政务服务平台的旧接口模块、或者对接某家老牌工业设备厂商的 OPC UA 配置工具——你大概率会突然被拉进一个会议室,听到这句话:“这个 XML Schema 是对方定死的,字段顺序、命名空间、空元素写法一个都不能改,下周要联调。”
这时候,别急着翻文档,更别想着“用 JSON 转一下再塞进去”——XML 不是 JSON 的包装盒,它是带语义、带结构约束、带命名空间、带处理指令、甚至带 DTD 验证规则的完整文档模型。 .NET 提供的 XmlSerializer 、 DataContractSerializer 、 XmlDocument 、 XDocument 四套机制,表面看都是“读写 XML”,实则分工明确、适用场景截然不同: XmlSerializer 适合强契约、类到 XML 的双向映射; DataContractSerializer 绑定 WCF 时代遗留系统,对 [DataMember(Order=)] 敏感; XmlDocument 是 DOM 模型,适合局部修改、XPath 查询; XDocument 是 LINQ to XML,写起来像 C# 代码,但内存占用高、不支持流式处理。
我去年帮一家电力调度中心重构 SCADA 系统配置模块,他们原有 XML 配置文件有 327 个节点,嵌套深度达 9 层,且要求:① 保留注释(用于运维标注);② 空元素必须写成 <Timeout></Timeout> 而非 <Timeout /> ;③ 所有时间字段必须按 yyyy-MM-ddTHH:mm:ss.fffK 格式且带时区;④ 某些字段需按业务规则动态生成 xsi:nil="true" 。这些需求,用 System.Text.Json 做不了,用 XDocument 写起来像在填表格,最后我们回归 XmlSerializer ,但加了 5 层自定义 IXmlSerializable 实现和 3 个 XmlAttributeOverrides 配置,才把所有坑踩平。
这篇总结不是罗列 API,而是告诉你: 什么时候该用哪个类、为什么不能混用、哪些属性组合会触发隐式行为、以及当 XML 规范和 .NET 默认行为打架时,你手里的扳手在哪 。适合正在啃老系统、对接政府/国企/制造业接口、或需要生成符合 ISO/IEC 标准 XML 文档的开发者。哪怕你只用过 JsonConvert.SerializeObject() ,也能从这里看懂 XML 序列化的底层逻辑。
2. 核心技术选型与设计思路拆解:四套机制的本质差异
2.1 XmlSerializer:契约驱动的“类-XML”双向翻译器
XmlSerializer 是 .NET Framework 时代最成熟的 XML 序列化方案,它的核心设计哲学是 “类即 Schema” —— 你定义的 C# 类结构,直接决定生成 XML 的根节点名、子元素顺序、是否允许为空等。它不依赖运行时反射遍历所有属性,而是在首次使用时动态编译生成一个临时程序集( XmlSerializationReader/Writer ),因此首次序列化稍慢,但后续极快。
关键限制在于:它只序列化 public 字段和属性 ,且要求类必须有 无参构造函数 (反序列化时需实例化对象)。它对 List<T> 、 Dictionary<TKey, TValue> 等泛型集合支持良好,但对 IList 、 IDictionary 接口类型会报错,必须指定具体实现类(如 List<string> )。
提示:
XmlSerializer默认将null字符串序列化为空元素<Name></Name>,而非跳过该节点。若需跳过,必须给属性加[XmlElement(IsNullable = true)],且该属性类型需为可空引用类型(C# 8+ 启用 nullable reference types 后,string?才算可空)。
2.2 DataContractSerializer:WCF 遗留系统的“契约优先”方案
DataContractSerializer 出生于 WCF 时代,设计理念是 “契约先行” —— 你先定义 [DataContract] 和 [DataMember] ,再生成类。它支持私有字段序列化(通过 [DataMember] 标记),不要求无参构造函数(反序列化时用 FormatterServices.GetUninitializedObject() 直接分配内存),且对循环引用、 DateTimeOffset 、 TimeSpan 等类型原生支持更好。
但它有一个致命短板: 不支持 XML 命名空间前缀控制 。比如你需要生成 <ns:Order xmlns:ns="http://example.com/order"> , XmlSerializer 可通过 [XmlRoot(Namespace = "http://example.com/order")] + [XmlSerializerNamespaces] 精确控制前缀,而 DataContractSerializer 只能指定命名空间 URI,前缀由序列化器随机生成(如 d1p1 ),这在对接严格校验命名空间前缀的政务系统时直接失败。
2.3 XmlDocument:基于 DOM 的“树形编辑器”
XmlDocument 是 W3C DOM 标准的 .NET 实现,它把整个 XML 加载进内存,构建成一棵节点树( XmlElement 、 XmlAttribute 、 XmlText 等)。优势在于: 可随机访问任意节点、支持 XPath 查询、可保留注释/CDATA/处理指令、支持就地修改 。
但代价巨大:内存占用是原始 XML 大小的 5~10 倍(每个节点都是托管对象),且没有内置的类映射能力。你得手动 doc.SelectSingleNode("//Order/Items") 找到节点,再逐个 itemNode.Attributes["id"].Value 取值,再 new 一个 C# 对象赋值——写起来像在操作 Excel 单元格。它适合的场景很窄:仅需修改 XML 中几个特定字段(如批量替换数据库连接字符串)、或需验证 XML 注释内容是否合规。
2.4 XDocument:LINQ to XML 的“函数式构建器”
XDocument 是 .NET 3.5 引入的轻量级 XML API,语法极度简洁:
var doc = new XDocument(
new XElement("Order",
new XAttribute("version", "2.0"),
new XElement("Customer", "张三"),
new XElement("Items


1597

被折叠的 条评论
为什么被折叠?



