1. 为啥要选择格式?简短提醒
任何 .NET 对象在内存里都是一堆地址、引用、字段、内部结构和其他“机器里面的东西”。但你不能就这样直接把它存成文件:文件要有一种结构,别的程序也能读(最好也能写)。这时候就得选序列化格式了。
序列化格式的主要任务:
- 紧凑:磁盘空间或网络传输速度。
- 可读性(给人看!):能不能用文本编辑器打开并理解内容。
- 通用性:能不能和其它系统、语言集成。
- 支持复杂结构:嵌套、集合、引用、类型等。
- 安全性和兼容性。
2. 二进制序列化 (Binary Serialization)
二进制序列化把数据表示成“原始”的字节流。想象你不仅把东西装箱,还把箱子打了水泥——很快、很紧凑,但如果没工具你连自己的袜子都找不到。
// 示例仅作历史参考:
using System.Runtime.Serialization.Formatters.Binary;
// ... 创建对象
var user = new User { Name = "伊万", Age = 42 };
// 序列化
using var fs = new FileStream("user.bin", FileMode.Create);
var formatter = new BinaryFormatter();
formatter.Serialize(fs, user);
优点:
- 紧凑:占用最少空间,速度很快。
- 内建支持“原生”的 .NET 类型。
缺点:
- 格式完全不可读——在记事本里打开你会进入“黑客世界”(也就是会看到不可读字符)。
- 绝对地 不通用:只适合 .NET(而且通常只对相同版本有效)。
- 对版本间兼容性很脆弱。
以前用在哪儿?
在 .NET 内部,需要在同一台机器的进程间快速“转储”对象时会用。现在强烈 不建议这么做,特别是当你关心安全性和长期维护时。在现代项目中使用 BinaryFormatter 是不好的实践。
3. XML 格式 (Extensible Markup Language)
XML是人和机器都能读的基于标签的格式,像 HTML,但没有那些关于 <body> 的笑话。
<User>
<Name>伊万</Name>
<Age>42</Age>
</User>
序列化示例:
using System.Xml.Serialization;
var user = new User { Name = "伊万", Age = 42 };
var serializer = new XmlSerializer(typeof(User));
using var fs = new FileStream("user.xml", FileMode.Create);
serializer.Serialize(fs, user);
优点:
- 可读:可以打开文件肉眼看出结构。
- 通用:很多语言和程序都能读 XML。
- 灵活(支持 schema、校验、命名空间等)。
- 适合复杂结构和嵌套对象。
缺点:
- “啰嗦”且笨重:占空间、消耗流量。
- 解析比二进制格式慢。
- 对某些类型(比如日期和时间)序列化可能不精确。
- 对某些集合或非标准对象需要额外配置。
在哪里用?
企业系统间的数据交换、配置文件、需要严格结构的集成场景。
4. JSON 格式 (JavaScript Object Notation)
JSON是紧凑、轻量、易读的格式,来自 JavaScript 世界,凭借简洁性和易用性几乎统治了数据交换领域。
{
"Name": "伊万",
"Age": 42
}
序列化示例:
using System.Text.Json;
var user = new User { Name = "伊万", Age = 42 };
string json = JsonSerializer.Serialize(user);
File.WriteAllText("user.json", json);
优点:
- 对人和机器都很可读。
- 容易和 web 技术集成(API、JavaScript 等)。
- 比 XML 更紧凑。
- 现代库下序列化/反序列化速度很快(System.Text.Json, Newtonsoft.Json)。
缺点:
- 不支持注释(你的 JSON 不能自嘲)。
- 结构上有限制:不能序列化对象间引用或某些复杂类型(比如非字符串键的字典)。
- 不同实现对日期/时间的处理可能不一致。
在哪里用?
几乎所有需要跨平台数据交换的地方:web 服务、移动应用、REST API 等。
5. CSV (Comma-Separated Values)
CSV是简单的文本格式,把数据表示成表格(行和列),字段用逗号或其他分隔符分开。
Name,Age
伊万,42
奥尔加,27
手工写 CSV 的示例:
// 为了简单,用普通 StreamWriter 写
var lines = new List<string>
{
"Name,Age",
"伊万,42",
"奥尔加,27"
};
File.WriteAllLines("users.csv", lines);
优点:
- 被大量程序支持(Excel、Google Docs、数据库等)。
- 简洁,适合表格型数据。
缺点:
- 不适合嵌套或复杂对象(只适合“扁平”结构)。
- 不保存类型信息(所有都是字符串)。
- 当数据包含逗号、引号或换行时,转义问题会很麻烦。
在哪里用?
数据库间的导入/导出、简单记录的交换、应用数据导出。
6. 强类型交换的协议和格式
Protocol Buffers (protobuf) 和 MessagePack 是带契约(schema)的二进制序列化格式,使序列化更紧凑、更快。它们需要预先描述数据结构的 schema。
简化的 protobuf 示例:
不属于 .NET 标准,需要包 Google.Protobuf。
message User {
string name = 1;
int32 age = 2;
}
优点:
- 极高的速度和最紧凑的表示。
- 跨平台(很多语言都支持 protobuf)。
缺点:
- 学习和配置更复杂。
- 需要根据 schema 生成代码。
- 如果不是每秒百万级消息,很多时候用不着。
在哪里用?
高负载系统、微服务间数据交换、游戏、IoT。
7. 有用的细节
格式对比表
| 格式 | 可读性 | 紧凑性 | 通用性 | 复杂对象 | .NET 9 标准 | 安全性 |
|---|---|---|---|---|---|---|
| Binary (.NET) | 否 | 是 | 否 | 是 | 否 | 否 |
| XML | 是 | 否 | 是 | 是 | 是 | 是 |
| JSON | 是 | 是 | 是 | 部分 | 是 | 是 |
| CSV | 是 | 是 | 是 | 否 | 否 | 是 |
| Protobuf | 否 | 最佳 | 是 | 是 | 否 | 是 |
怎么为你的应用选格式?
需要保存复杂数据结构且你希望人能读 — 用 XML 或 JSON。
需要在你控制的服务间快速传输大量数据 — 考虑 protobuf 或 MessagePack。
为 Excel 或数据库导出报表 — 用 CSV。
写服务或 CI/CD 的配置文件 — YAML 会很方便你。
紧凑、速度和对象间引用非常关键?那就找二进制格式,但别忘了安全性和兼容性的缺点。
8. 棘手的点和常见错误
如果你因为“保密”就把数据存为二进制,记住:二进制转存不等于安全。攻击者仍然可以分析这个字节流。需要安全时,还是用加密。
常见问题:在处理文本格式(XML/JSON/CSV)时忘记编码——请使用 UTF-8。并非所有编辑器和系统都喜欢 BOM(字节顺序标记)。
XML 看起来“老”,但它在契约式协议里很棒,那里结构校验很重要(比如 XSD schema);而 JSON 适合快速、灵活的数据交换。
序列化复杂对象集合时,并非所有格式都正确支持嵌套或递归结构。JSON 和 XML 对数组与列表支持良好,CSV 则不行。
GO TO FULL VERSION