序列化和反序列化是 C# 开发中核心的数据处理技术,广泛应用于数据持久化、跨进程通信、网络传输等场景。本文将从核心概念、主流实现方式、实战案例到最佳实践,全方位拆解 C# 序列化 / 反序列化技术,帮你彻底掌握其原理与应用。
一、核心概念:什么是序列化与反序列化?
1. 序列化(Serialization)
将内存中的对象(如自定义类实例、集合、结构体等)转换为可存储 / 可传输的格式(如二进制、XML、JSON、字节流)的过程。核心目的:突破内存限制,实现对象的持久化(存文件 / 数据库)或跨平台 / 跨进程传输(网络通信、RPC 调用)。
2. 反序列化(Deserialization)
将序列化后的字节流 / 文本数据还原为内存中原始对象的过程。核心要求:反序列化时必须能访问对象的类型定义,否则无法还原(如缺少类定义会抛出异常)。
3. 核心价值
- 数据持久化:将对象保存到文件、数据库,程序重启后可恢复;
- 跨进程通信:进程间、客户端 - 服务器间传输对象(如 WebAPI 返回 JSON 对象);
- 分布式系统:微服务间调用时传递复杂数据结构;
- 缓存 / 状态管理:将对象存入 Redis 等缓存中间件。
二、C# 主流序列化方式:特性、实现与对比
C# 提供了多种序列化方案,适配不同场景(性能、可读性、跨平台),以下是最常用的 4 种:
1. 二进制序列化(Binary Serialization)
核心特点
- 基于
System.Runtime.Serialization.Formatters.Binary命名空间; - 将对象转换为紧凑的二进制字节流,体积小、速度快;
- 仅限.NET 平台(不跨语言),适合.NET 进程间通信 / 本地持久化;
- 需为序列化类标记
[Serializable]特性,不支持无参构造函数的类(反序列化时会绕过构造函数)。
实现步骤
步骤 1:定义可序列化的类
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
// 必须标记[Serializable],否则序列化失败
[Serializable]
public class User
{
// 字段会被序列化(属性的自动字段也会)
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreateTime { get; set; }
// 可选:标记[NonSerialized]排除不需要序列化的字段
[NonSerialized]
private string _password; // 密码不序列化
}
步骤 2:序列化(对象→二进制流)
/// <summary>
/// 二进制序列化对象到文件
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <param name="obj">待序列化对象</param>
/// <param name="filePath">保存路径</param>
public static void BinarySerialize<T>(T obj, string filePath)
{
if (obj == null) throw new ArgumentNullException(nameof(obj));
using (FileStream fs = new FileStream(filePath, FileMode.Create))
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(fs, obj); // 写入二进制流
}
}
步骤 3:反序列化(二进制流→对象)
/// <summary>
/// 从文件反序列化二进制数据为对象
/// </summary>
/// <typeparam name="T">目标类型</typeparam>
/// <param name="filePath">文件路径</param>
/// <returns>还原后的对象</returns>
public static T BinaryDeserialize<T>(string filePath)
{
if (!File.Exists(filePath)) throw new FileNotFoundException("文件不存在", filePath);
using (FileStream fs = new FileStream(filePath, FileMode.Open))
{
BinaryFormatter formatter = new BinaryFormatter();
return (T)formatter.Deserialize(fs); // 还原对象
}
}
步骤 4:调用示例
// 序列化
var user = new User { Id = 1, Name = "张三", CreateTime = DateTime.Now };
BinarySerialize(user, "user.bin");
// 反序列化
var restoredUser = BinaryDeserialize<User>("user.bin");
Console.WriteLine($"反序列化结果:ID={restoredUser.Id},姓名={restoredUser.Name}");
注意事项
BinaryFormatter在.NET 5 + 中被标记为过时(安全风险:反序列化不可信数据可能导致注入攻击);- 仅适用于.NET 内部场景,不推荐跨平台 / 公网使用。
2. XML 序列化(XmlSerializer)
核心特点
- 基于
System.Xml.Serialization命名空间; - 将对象转换为人类可读的 XML 文本,跨平台 / 跨语言(如 Java 可解析);
- 无需标记
[Serializable],但要求类有无参构造函数; - 可通过特性自定义 XML 结构(如节点名、忽略字段)。
实现步骤
步骤 1:定义类(无需标记 [Serializable])
using System.Xml.Serialization;
public class Product
{
// 无参构造函数(必须)
public Product() { }
// 自定义XML节点名
[XmlAttribute("product-id")] // 作为XML属性
public int Id { get; set; }
[XmlElement("产品名称")] // 作为XML元素
public string Name { get; set; }
public decimal Price { get; set; }
// 忽略序列化
[XmlIgnore]
public string Remark { get; set; }
}
步骤 2:序列化 / 反序列化工具方法
/// <summary>
/// XML序列化对象到文件
/// </summary>
public static void XmlSerialize<T>(T obj, string filePath)
{
if (obj == null) throw new ArgumentNullException(nameof(obj));
using (FileStream fs = new FileStream(filePath, FileMode.Create))
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
serializer.Serialize(fs, obj);
}
}
/// <summary>
/// 从文件反序列化XML为对象
/// </summary>
public static T XmlDeserialize<T>(string filePath)
{
if (!File.Exists(filePath)) throw new FileNotFoundException("文件不存在", filePath);
using (FileStream fs = new FileStream(filePath, FileMode.Open))
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(fs);
}
}
步骤 3:调用示例
// 序列化
var product = new Product
{
Id = 1001,
Name = "笔记本电脑",
Price = 5999.99m,
Remark = "内部备注" // 会被忽略
};
XmlSerialize(product, "product.xml");
// 反序列化
var restoredProduct = XmlDeserialize<Product>("product.xml");
Console.WriteLine($"XML反序列化:ID={restoredProduct.Id},名称={restoredProduct.Name},价格={restoredProduct.Price}");
生成的 XML 文件内容:
<?xml version="1.0"?>
<Product xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" product-id="1001">
<产品名称>笔记本电脑</产品名称>
<Price>5999.99</Price>
</Product>
适用场景
- 配置文件存储(如自定义 XML 配置);
- 跨语言系统间的数据交换(如.NET 与 Java 对接);
- 需要可读性的持久化场景。
3. JSON 序列化(System.Text.Json/Newtonsoft.Json)
JSON 是当前最主流的序列化格式,C# 提供两种核心实现:
- System.Text.Json:.NET Core 3.0 + 内置,高性能、轻量;
- Newtonsoft.Json(Json.NET):第三方库,功能更丰富,兼容所有.NET 版本。
方式 1:System.Text.Json(推荐)
步骤 1:定义类(无特殊标记)
public class Order
{
public long OrderId { get; set; }
public string Customer { get; set; }
public DateTime OrderTime { get; set; }
public List<string> Items { get; set; } = new List<string>();
}
步骤 2:序列化 / 反序列化
using System.Text.Json;
// 配置序列化选项(可选:美化格式、忽略空值、日期格式等)
var options = new JsonSerializerOptions
{
WriteIndented = true, // 美化输出
IgnoreNullValues = true, // 忽略空值字段
PropertyNameCaseInsensitive = true // 反序列化时忽略大小写
};
// 序列化:对象→JSON字符串
var order = new Order
{
OrderId = 2025001,
Customer = "李四",
OrderTime = DateTime.Now,
Items = new List<string> { "手机", "耳机" }
};
string jsonStr = JsonSerializer.Serialize(order, options);
File.WriteAllText("order.json", jsonStr);
// 反序列化:JSON字符串→对象
string jsonContent = File.ReadAllText("order.json");
var restoredOrder = JsonSerializer.Deserialize<Order>(jsonContent, options);
Console.WriteLine($"JSON反序列化:订单ID={restoredOrder.OrderId},客户={restoredOrder.Customer}");
生成的 JSON 内容:
{
"OrderId": 2025001,
"Customer": "李四",
"OrderTime": "2025-12-18T10:30:00.0000000+08:00",
"Items": [
"手机",
"耳机"
]
}
方式 2:Newtonsoft.Json(Json.NET)
需先安装 NuGet 包:Install-Package Newtonsoft.Json
using Newtonsoft.Json;
// 序列化
string jsonStr = JsonConvert.SerializeObject(order, Formatting.Indented);
File.WriteAllText("order.json", jsonStr);
// 反序列化
var restoredOrder = JsonConvert.DeserializeObject<Order>(jsonContent);
核心优势
- 轻量、高效、跨平台;
- 支持复杂类型(如动态对象、匿名类型);
- 广泛用于 WebAPI、前后端交互、微服务通信。
4. 协议缓冲区(Protobuf)
核心特点
- 谷歌开源的二进制序列化协议(
Google.Protobuf); - 比 JSON/XML 更小、更快,跨语言(C#/Java/Go/Python 等);
- 需定义
.proto文件描述数据结构,适合高性能跨平台通信。
快速实现
- 安装 NuGet 包:
Install-Package Google.Protobuf - 定义
.proto文件(如Person.proto):
syntax = "proto3";
message Person {
int32 id = 1;
string name = 2;
string email = 3;
repeated string hobbies = 4; // 列表
}
- 通过工具生成 C# 类,再进行序列化 / 反序列化(篇幅限制,详细步骤可参考 Protobuf 官方文档)。
主流序列化方式对比
| 序列化方式 | 可读性 | 性能 | 跨平台 / 跨语言 | 适用场景 |
|---|---|---|---|---|
| 二进制序列化 | 差 | 高 | 否 | .NET 内部进程通信、本地持久化 |
| XML 序列化 | 高 | 中 | 是 | 配置文件、跨语言数据交换 |
| JSON 序列化 | 高 | 高 | 是 | WebAPI、前后端交互、微服务 |
| Protobuf | 差 | 极高 | 是 | 高性能跨平台通信、大数据传输 |
三、关键特性与进阶技巧
1. 控制序列化范围
- 排除字段 / 属性:
- 二进制:
[NonSerialized](字段); - XML:
[XmlIgnore]; - JSON:
[JsonIgnore](System.Text.Json)/[JsonProperty(NullValueHandling = NullValueHandling.Ignore)](Newtonsoft)。
- 二进制:
- 只序列化特定属性:XML/JSON 可通过自定义契约实现(如
DataContract)。
2. 版本兼容性
当类结构变更(如新增字段、重命名属性),反序列化旧数据时可能出错:
- 二进制序列化:推荐使用
[OptionalField]标记新增字段; - JSON/XML:设置忽略未知属性(如
JsonSerializerOptions.IgnoreUnknownFields = true)。
3. 处理循环引用
对象间循环引用(如 A 包含 B,B 包含 A)会导致序列化失败:
- JSON:启用循环引用处理
// System.Text.Json var options = new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.Preserve }; // Newtonsoft.Json JsonConvert.SerializeObject(obj, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });
4. 自定义序列化逻辑
通过实现接口自定义序列化规则:
- 二进制:
ISerializable; - JSON:
JsonConverter(System.Text.Json)/JsonConverter<T>(Newtonsoft); - XML:
IXmlSerializable。
示例(JSON 自定义日期格式):
public class CustomDateTimeConverter : JsonConverter<DateTime>
{
private readonly string _format = "yyyy-MM-dd HH:mm:ss";
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return DateTime.Parse(reader.GetString()!);
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString(_format));
}
}
// 使用自定义转换器
var options = new JsonSerializerOptions
{
Converters = { new CustomDateTimeConverter() },
WriteIndented = true
};
string json = JsonSerializer.Serialize(order, options);
四、实战应用场景
1. 数据持久化
将对象保存到本地文件 / 数据库,程序重启后恢复状态:
// 保存用户配置
var config = new AppConfig { Theme = "Dark", FontSize = 14, AutoSave = true };
string configJson = JsonSerializer.Serialize(config);
File.WriteAllText("appconfig.json", configJson);
// 程序启动时加载配置
if (File.Exists("appconfig.json"))
{
string json = File.ReadAllText("appconfig.json");
var loadedConfig = JsonSerializer.Deserialize<AppConfig>(json);
// 应用配置...
}
2. WebAPI 数据传输
ASP.NET Core WebAPI 默认使用 System.Text.Json 序列化 / 反序列化:
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
// 序列化:返回User对象自动转为JSON
[HttpGet("{id}")]
public IActionResult GetUser(int id)
{
var user = new User { Id = id, Name = "张三" };
return Ok(user); // 自动序列化为JSON响应
}
// 反序列化:请求体JSON自动转为User对象
[HttpPost]
public IActionResult AddUser([FromBody] User user)
{
// 处理用户数据...
return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
}
}
3. 分布式缓存(Redis)
将对象序列化后存入 Redis,提升读取性能:
using StackExchange.Redis;
// 连接Redis
var redis = ConnectionMultiplexer.Connect("localhost:6379");
var db = redis.GetDatabase();
// 序列化对象为JSON字符串,存入Redis
var product = new Product { Id = 1001, Name = "手机", Price = 2999 };
string json = JsonSerializer.Serialize(product);
db.StringSet("product:1001", json, TimeSpan.FromHours(1)); // 1小时过期
// 从Redis读取并反序列化
string cachedJson = db.StringGet("product:1001");
if (!string.IsNullOrEmpty(cachedJson))
{
var cachedProduct = JsonSerializer.Deserialize<Product>(cachedJson);
}
4. 跨进程通信(IPC)
通过命名管道 / 共享内存传输序列化后的对象:
// 进程1:序列化对象并写入命名管道
using (var pipe = new NamedPipeServerStream("MyPipe"))
{
pipe.WaitForConnection();
var user = new User { Id = 1, Name = "张三" };
string json = JsonSerializer.Serialize(user);
byte[] data = Encoding.UTF8.GetBytes(json);
pipe.Write(data, 0, data.Length);
}
// 进程2:读取管道数据并反序列化
using (var pipe = new NamedPipeClientStream("MyPipe"))
{
pipe.Connect();
byte[] buffer = new byte[1024];
int bytesRead = pipe.Read(buffer, 0, buffer.Length);
string json = Encoding.UTF8.GetString(buffer, 0, bytesRead);
var user = JsonSerializer.Deserialize<User>(json);
}
五、安全与性能最佳实践
1. 安全注意事项
- 避免反序列化不可信数据(如网络请求中的二进制 / XML 数据):
BinaryFormatter存在严重安全风险,优先使用 JSON/Protobuf; - 验证反序列化后的对象:检查字段值是否合法(如 ID 是否为正数、字符串长度是否合规);
- 限制反序列化类型:通过自定义序列化器限制可还原的类型,防止类型注入攻击。
2. 性能优化
- 复用序列化器实例:
XmlSerializer/JsonSerializer创建成本高,建议缓存(如静态变量); - 选择合适的序列化格式:高性能场景用 Protobuf / 二进制,跨平台用 JSON;
- 忽略不必要的字段:减少序列化数据体积,提升传输 / 存储效率;
- 异步序列化 / 反序列化:IO 密集型场景(如文件 / 网络)使用
SerializeAsync/DeserializeAsync。
3. 常见坑点
- 反序列化时缺少无参构造函数:XML/JSON 序列化要求类有公共无参构造函数;
- 循环引用未处理:导致栈溢出或序列化失败;
- 版本不一致:类结构变更后反序列化旧数据出错,需预留兼容性字段;
- 敏感数据序列化:密码、令牌等敏感信息需加密后再序列化,或直接忽略。
六、总结
C# 序列化 / 反序列化是处理对象数据的核心技术,选择合适的方式是关键:
- 本地 /.NET 内部场景:二进制序列化(注意安全);
- 需可读性 / 跨语言:XML(配置)、JSON(Web);
- 高性能跨平台:Protobuf;
- Web / 前后端交互:优先 System.Text.Json(.NET Core+)或 Newtonsoft.Json(兼容旧版本)。
掌握序列化的核心原理、自定义规则和最佳实践,能有效解决数据持久化、跨系统通信等开发中的核心问题,提升代码的可扩展性和兼容性。
728

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



