1. 介绍
提醒:序列化是把对象的状态保存成字符串、文件或流的一种方式,以便以后可以恢复该对象(反序列化)——这样你就可以重新得到具有相同数据的对象。
典型场景:
1) 你有一个对象,例如一个包含名字和年龄的用户。
2) 需要保存它——通过网络传输、写入文件,或者比如转换成 JSON 格式。
3) 然后从字符串/文件里把数据拿回来——你又得到一个对象!
在 C# 中这只需几行代码。我们来看看具体怎么做。
我们的应用
我们在开发一个小应用“联系人管理器”。假设我们有一个要序列化的类 Person。
public class Person
{
// 公共属性对序列化是必须的!
public string Name { get; set; }
public int Age { get; set; }
// 无参数构造函数对 XmlSerializer 是必须的
public Person() { }
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
如果你不熟悉这种语法别担心——在课程后面会详细讲解类和属性。现在把它当作实验用的数据模型。注意:公共属性 Name 和 Age 对序列化很重要,而 XmlSerializer 需要一个公共的无参构造函数。
2. 使用 System.Text.Json 序列化为 JSON
最简单的例子
说了就给你看!下面是序列化为 JSON 的样子:
using System;
using System.Text.Json;
Person person = new Person("阿丽萨", 28);
// 序列化:把对象变成 JSON 字符串
string json = JsonSerializer.Serialize(person);
Console.WriteLine("JSON:");
Console.WriteLine(json);
屏幕上会显示什么?
JSON:
{"Name":"阿丽萨","Age":28}
很简单:对象 → JSON。就是这么神奇!
反序列化 —— 把东西还原回来
现在做反向操作——从 JSON 字符串恢复对象:
string json = "{\"Name\":\"鲍勃\",\"Age\":35}";
// 反序列化:从 JSON 字符串恢复出 Person 类型的对象
Person restored = JsonSerializer.Deserialize<Person>(json);
Console.WriteLine("恢复的对象:");
Console.WriteLine($"名字: {restored.Name}, 年龄: {restored.Age}");
结果:
恢复的对象:
名字: 鲍勃, 年龄: 35
注意:如果数据结构不匹配(例如没有某个属性),对应字段会保留默认值(引用类型为 null,数值类型为 0 等)。
3. 使用 XmlSerializer 进行 XML 的序列化/反序列化
第一次看 XML 序列化
using System;
using System.IO;
using System.Xml.Serialization;
Person person = new Person("卡佳", 22);
// 为 Person 类型创建序列化器
var serializer = new XmlSerializer(typeof(Person));
// 把 XML 写入文件
using (var stream = new FileStream("person.xml", FileMode.Create))
{
serializer.Serialize(stream, person);
// using 块结束时流会自动关闭
}
Console.WriteLine("XML 文件已创建!");
在文件 person.xml 中会出现类似这样的内容:
<?xml version="1.0"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>卡佳</Name>
<Age>22</Age>
</Person>
重要点:
写入文件时使用的是流(Stream,例如 FileStream)。如果你想把序列化结果放到字符串里(比如要通过网络发送),可以用 StringWriter:
var serializer = new XmlSerializer(typeof(Person));
using (var sw = new StringWriter())
{
serializer.Serialize(sw, person);
string xmlString = sw.ToString();
Console.WriteLine(xmlString);
}
从 XML 反序列化
using (var stream = new FileStream("person.xml", FileMode.Open))
{
// 别忘了转换到需要的类型!
Person restored = (Person)serializer.Deserialize(stream);
Console.WriteLine($"名字: {restored.Name}, 年龄: {restored.Age}");
}
再提醒一次:XmlSerializer 要求被序列化的类必须有公共的默认(无参)构造函数。
4. 文件与字符串的处理:选哪个?
在真实场景中,序列化可能直接输出为字符串(用于网络传输/数据库存储)或写入文件(用于长期保存)。
序列化为字符串(例如,用于通过 API 传输):
- JSON:用 JsonSerializer.Serialize(obj)。
- XML:用 StringWriter。
序列化到文件(例如导出/备份):
- JSON:把字符串用常规文件写入方法保存到文件。
- XML:直接用 XmlSerializer 写到流里。
示例:序列化到文件并恢复(JSON)
using System.IO;
using System.Text.Json;
Person person = new Person("汤姆", 42);
// 序列化为字符串
string json = JsonSerializer.Serialize(person);
// 写入文件
File.WriteAllText("contact.json", json);
// 从文件读取
string readJson = File.ReadAllText("contact.json");
Person restored = JsonSerializer.Deserialize<Person>(readJson);
Console.WriteLine($"{restored.Name}, 年龄: {restored.Age}");
5. JSON 与 XML 的比较
过程可视化
为了不搞混,这里是序列化/反序列化过程的示意:
flowchart LR
A[对象] -- 序列化 --> B[字符串/文件/流]
B -- 反序列化 --> C[对象]
A -. JSON 或 XML .-> B
B -. JSON 或 XML .-> C
可以把序列化想象成“收拾行李”,反序列化就是“到家后把行李 unpack(拆包)”。
比较表
| 功能 | JSON (System.Text.Json) | XML (XmlSerializer) |
|---|---|---|
| 读/写字符串 | ++ | + |
| 读/写文件 | ++ | ++ |
| 需要无参构造函数 | 否 | 是 |
| 对人类可读性 | ++ | + |
| 严格的数据模式(校验) | - | ++ |
| 定制属性的灵活性 | + | ++ |
| 性能 | ++ | + |
| 与互联网兼容性 | ++ | + |
++ — 非常好,+ — 正常,- — 不支持
6. 如何在我们的应用里添加序列化
假设在“联系人管理器”里增加了把用户联系人列表保存到文件并恢复的功能。
上下文:我们有一个联系人列表(List<Person>)。
List<Person> contacts = new List<Person>
{
new Person("瓦西娅", 30),
new Person("彼佳", 25),
new Person("玛莎", 27)
};
// 保存所有联系人 —— 序列化为 JSON
string jsonContacts = JsonSerializer.Serialize(contacts);
File.WriteAllText("contacts.json", jsonContacts);
// 恢复联系人列表
string readContactsJson = File.ReadAllText("contacts.json");
List<Person> restored = JsonSerializer.Deserialize<List<Person>>(readContactsJson);
foreach (var c in restored)
{
Console.WriteLine($"{c.Name}, 年龄: {c.Age}");
}
同样的事情也可以用 XML 做,但有些细节不太一样。使用 XmlSerializer 序列化集合时通常会把列表包在一个单独的容器类(根元素)里——这样更容易控制格式和元素名称。
7. 序列化和反序列化的常见错误
错误 #1:序列化和反序列化时类型不匹配。
你序列化的是 List<Person> —— 那么反序列化也必须是 List<Person>,而不是 List<object> 或 IEnumerable<Person>。否则会报错或得到意外结果。
错误 #2:尝试序列化非公共字段或属性。
常见序列化器(JsonSerializer, XmlSerializer)处理公共成员。如果属性没有公共的 getter/setter,值可能不会进入 JSON/XML,然后你会疑惑为什么是 null。
错误 #3:缺少无参构造函数。
对 XmlSerializer 来说,公共的默认构造函数是必须的。没有的话反序列化会抛异常。
错误 #4:错误理解对象引用。
如果一个对象引用另一个对象,序列化是“按值”进行的(作为嵌套对象)。引用本身不会被保存,这可能导致数据重复或循环引用问题。
错误 #5:尝试混用序列化格式。
XML 和 JSON 是不同的格式和序列化器。不能把 JSON 字符串交给 XmlSerializer,反之亦然——会报错。
GO TO FULL VERSION