1. 介绍
XML 是 eXtensible Markup Language 的缩写。想象一个既严格又健谈的 JSON,不停地重复“我有结构,相信我!”。XML 是一种文本格式,数据被标签包裹着:
<Player>
<Name>Aragorn</Name>
<Health>100</Health>
</Player>
为什么现在还要把对象序列化成 XML?
- XML 仍然在大型(经常是“遗留”)系统之间的集成中广泛使用,特别是需要严格按模式(XSD)校验的场景。
- 在企业配置中常常用到 XML(例如在老版本 .NET 中的 App.config 和 Web.config)。
- 当你需要数据结构“自描述”时,XML 很合适。
有趣的事实: XML 的设计初衷就是要尽可能“可扩展”和可验证;这也就是它名字的来源。可以对它按模式做校验——这是 JSON 默认不具备的。
XmlSerializer:主角
在 .NET 里负责对象到 XML 序列化的是类 System.Xml.Serialization.XmlSerializer。
- 它把对象的 public 属性和字段变成 XML 元素,反过来也能根据 XML 恢复对象。
- 只对公开的类型和成员有效。
- 不会序列化 private 字段和属性 —— 只有公开的成员会被处理!
- 被序列化的类需要有一个无参的构造函数。
注意: 如果你的类不满足这些要求,XmlSerializer 将无法工作——在序列化或反序列化时会抛出异常。
2. 简单示例 — 把对象序列化成 XML
我们来看看怎么把我们常用的类 Player 序列化成 XML。
步骤 1. 描述类
public class Player
{
public string Name { get; set; }
public int Health { get; set; }
public bool IsAlive { get; set; }
// 重要:需要一个 public 的无参构造函数!
public Player() { }
// 可选:为了方便
public Player(string name, int health, bool isAlive)
{
Name = name;
Health = health;
IsAlive = isAlive;
}
}
步骤 2. 序列化到文件
using System.Xml.Serialization;
Player aragorn = new Player("Aragorn", 100, true);
// 1. 创建针对 Player 类型的序列化器
XmlSerializer serializer = new XmlSerializer(typeof(Player));
// 2. 打开文件写入(不存在时会创建)
using FileStream fs = new FileStream("player.xml", FileMode.Create);
// 3. 把对象序列化为 XML
serializer.Serialize(fs, aragorn);
结果 (player.xml):
<?xml version="1.0"?>
<Player xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>Aragorn</Name>
<Health>100</Health>
<IsAlive>true</IsAlive>
</Player>
最简单流程的可视化示意
内存中的对象 ---> XmlSerializer.Serialize() ---> 磁盘上的 XML 文件
3. 反序列化 — 从 XML “复活” 对象
当你需要读取之前序列化的对象时,过程正好相反:
using FileStream fs = new FileStream("player.xml", FileMode.Open);
XmlSerializer serializer = new XmlSerializer(typeof(Player));
Player player = (Player)serializer.Deserialize(fs);
Console.WriteLine($"姓名: {player.Name}, 生命值: {player.Health}, 存活: {player.IsAlive}");
程序会输出: 姓名: Aragorn, 生命值: 100, 存活: True
4. 序列化到字符串 — 不仅仅是文件
序列化到字符串
不一定非要写到文件。有时候你需要把 XML 作为字符串发到网络上,或显示在界面上。
using System.IO;
using System.Xml.Serialization;
Player player = new Player("Frodo", 42, true);
XmlSerializer serializer = new XmlSerializer(typeof(Player));
StringWriter stringWriter = new StringWriter();
serializer.Serialize(stringWriter, player);
string xmlString = stringWriter.ToString();
Console.WriteLine(xmlString); // 整个 XML 字符串会显示在屏幕上!
从字符串反序列化
同样可以从字符串而不是文件反序列化对象:
string xml = "<Player Name=\"Frodo\"><HP>42</HP></Player>";
XmlSerializer serializer = new XmlSerializer(typeof(Player));
using StringReader stringReader = new StringReader(xml);
Player frodo = (Player)serializer.Deserialize(stringReader);
Console.WriteLine(frodo.Name); // Frodo
5. 自定义 XML:用属性控制结构
有时候默认行为不够好。生成的 XML 太啰嗦,或者标签名和目标不一致。这时可以用各种“魔法”属性直接告诉序列化器如何表示类的成员。
最常用的:
— 指定元素名。[XmlElement]
— 把字段作为 XML 属性,而不是元素。[XmlAttribute]
— 从序列化中排除该属性。[XmlIgnore]
和[XmlArray]
— 控制集合的包装和内部元素名。[XmlArrayItem]
使用示例:
public class Player
{
[XmlAttribute]
public string Name { get; set; }
[XmlElement("HP")]
public int Health { get; set; }
[XmlIgnore]
public bool IsTempSessionPlayer { get; set; }
}
生成的 XML:
<Player Name="Aragorn">
<HP>100</HP>
</Player>
常用序列化属性表
| 属性 | 影响 | 示例用法 |
|---|---|---|
|
把属性变成 XML 属性 | |
|
改变标签名 | 而不是 |
|
完全从序列化中排除该属性 | — |
|
改变集合外层包装的名字 | |
|
改变集合中元素的名字 | |
复杂集合序列化示例:
public class GameWorld
{
[XmlArray("Heroes")]
[XmlArrayItem("Hero")]
public List<Player> Players { get; set; } = new List<Player>();
}
XML:
<GameWorld>
<Heroes>
<Hero Name="Legolas">
<HP>90</HP>
</Hero>
<!-- ... -->
</Heroes>
</GameWorld>
更多关于自定义 XML 的内容我们会在后面的课里讲! :P
GO TO FULL VERSION