C# 序列化与反序列化详解:System.Text.Json、Newtonsoft.Json、XmlSerializer 用法、特性选项与安全实践

C# 序列化与反序列化详解:System.Text.Json、Newtonsoft.Json、XmlSerializer 用法、特性选项与安全实践

什么是序列化? 序列化就是把内存中的对象转换成可以存储或传输的格式 (如 JSON、XML、二进制),反序列化则是把这些数据还原成内存对象。可以通俗理解为:序列化是"打包",反序列化是"拆包"。无论是把数据存到文件/数据库、调用 Web API,还是做微服务通信,本质上都离不开序列化。本文从"为什么需要序列化"讲起,系统梳理 C# 中各种序列化方式、常用特性与选项、安全与最佳实践,适合初学者入门,也可作为开发查阅手册。


目录

  1. 为什么需要序列化
  2. 序列化基本语法
  3. 核心机制:特性与选项
  4. 安全与最佳实践
  5. 小结

1. 为什么需要序列化?

  • 序列化:将对象转换为可存储或传输的格式(如 JSON、XML、二进制)。
  • 反序列化:将存储或传输的格式还原为内存对象。
痛点 说明 后果
对象易失 内存对象随进程结束而销毁 无法持久化到磁盘、数据库或缓存
无法直接传输 对象引用不能跨进程、跨机器传递 分布式通信、Web API 难以实现
手动读写繁琐 逐个字段拼装与解析 易出错,字段变更后容易不同步
跨语言互操作差 不同语言对象模型不一致 系统集成成本高,格式难统一

序列化的解决方案:

将对象状态转换为标准格式,在需要时再反序列化恢复。最常用 JSON(System.Text.Json、Newtonsoft.Json),也支持 XML、二进制、Protobuf 等格式。

下图展示了序列化与反序列化的完整数据流转过程:内存对象经序列化为 JSON/XML,可用于持久化存储或网络传输,需要时再反序列化还原为对象。

【插图:序列化与反序列化数据流转图】 (发布时在此插入图片 序列化与反序列化数据流转图.png)


2. 序列化基本语法

2.1 常用序列化方式

方式 命名空间/包 格式 特点 建议
System.Text.Json System.Text.Json JSON .NET 内置,高性能,支持源生成/AOT 默认首选
Newtonsoft.Json Newtonsoft.Json NuGet JSON 功能丰富,兼容性强,配置灵活 老项目或需要高级特性时使用
XmlSerializer System.Xml.Serialization XML 内置,要求公共无参构造,序列化公共成员 传统 XML 接口
DataContractSerializer System.Runtime.Serialization XML 支持 [DataContract] 选择加入,WCF 常用 契约式 XML
BinaryFormatter System.Runtime.Serialization.Formatters.Binary 二进制 已弃用,不安全 禁止用于不可信数据
Protobuf protobuf-net / Google.Protobuf 二进制 高性能、跨语言、体积小 微服务、高性能通信

2.2 模型设计规范

规范 说明
使用公共属性 System.Text.Json 默认序列化公共属性,不序列化公共字段,除非 IncludeFields = true
提供构造函数 反序列化可用无参构造,或带参构造 + [JsonConstructor]
避免循环引用 使用 [JsonIgnore] 或 ReferenceHandler
使用 DTO 不要直接序列化 EF 实体或领域模型,避免暴露过度和循环引用
初始化集合/字符串 避免反序列化后出现 null 导致空引用
版本兼容 新增成员尽量可空或带默认值,反序列化默认忽略未知成员

2.3 使用示例

最基础的对象 → JSON → 对象:

csharp 复制代码
using System.Text.Json;

public class User
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public DateTime CreatedAt { get; set; }
}

var user = new User
{
    Id = 1,
    Name = "Alice",
    CreatedAt = DateTime.UtcNow
};

// 序列化:对象 → JSON 字符串
string json = JsonSerializer.Serialize(user, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(json);

// 反序列化:JSON 字符串 → 对象
User? copy = JsonSerializer.Deserialize<User>(json);
Console.WriteLine(copy?.Name);   // Alice

序列化后得到的 JSON 大致如下(控制台默认保持属性原名,日期为 ISO 8601 格式):

json 复制代码
{
  "Id": 1,
  "Name": "Alice",
  "CreatedAt": "2026-09-27T08:30:00.0000000Z"
}

带常用选项的写法:

csharp 复制代码
using System.Text.Json;
using System.Text.Json.Serialization;

var options = new JsonSerializerOptions
{
    WriteIndented = true,                                  // 缩进输出,便于阅读
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,     // 属性名转小驼峰
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull  // null 值不输出
};

string json = JsonSerializer.Serialize(user, options);
User? copy = JsonSerializer.Deserialize<User>(json, options);

Newtonsoft.Json 的写法:

csharp 复制代码
// 需先安装 NuGet 包:Newtonsoft.Json
using Newtonsoft.Json;

string json = JsonConvert.SerializeObject(user, Formatting.Indented);
User? copy = JsonConvert.DeserializeObject<User>(json);

XmlSerializer 的写法:

csharp 复制代码
using System.Xml.Serialization;

var serializer = new XmlSerializer(typeof(User));

using var writer = new StringWriter();
serializer.Serialize(writer, user);   // 对象 → XML
string xml = writer.ToString();

using var reader = new StringReader(xml);
User? copy = (User?)serializer.Deserialize(reader);  // XML → 对象

3. 核心机制:特性与选项(Attributes & Options)

序列化框架通常通过特性 控制单个成员的映射,通过选项控制全局行为。没有配置时,框架只按默认规则处理公共属性。

3.1 常用特性总览

特性 所属库 作用 示例
[JsonPropertyName] System.Text.Json 指定 JSON 属性名 [JsonPropertyName("user_name")]
[JsonIgnore] System.Text.Json 忽略某个属性 [JsonIgnore] public decimal Cost { get; set; }
[JsonInclude] System.Text.Json 包含非公共访问器 [JsonInclude] public string Name { get; private set; }
[JsonConverter] System.Text.Json 指定自定义转换器 [JsonConverter(typeof(JsonStringEnumConverter))]
[JsonConstructor] System.Text.Json 指定反序列化构造函数 [JsonConstructor] public User(int id) { }
[JsonExtensionData] System.Text.Json 捕获未知 JSON 属性 public Dictionary<string, JsonElement>? Extra { get; set; }
[JsonPolymorphic] / [JsonDerivedType] System.Text.Json 多态序列化,.NET 7+ [JsonDerivedType(typeof(Dog), "dog")]
[JsonProperty] Newtonsoft.Json 指定名称、顺序、是否必需 [JsonProperty("user_name")]
[DataContract] / [DataMember] System.Runtime.Serialization 选择加入,控制名称/顺序 [DataMember(Name = "name", Order = 1)]
[XmlElement] / [XmlAttribute] / [XmlIgnore] System.Xml.Serialization 控制 XML 映射 [XmlAttribute("id")]
[Serializable] System 标记可二进制序列化(旧) 不推荐用于 BinaryFormatter
[ProtoContract] / [ProtoMember] protobuf-net Protobuf 契约 [ProtoMember(1)]

3.2 常用选项总览

选项 作用 常用值
WriteIndented 输出缩进 JSON true
PropertyNamingPolicy 属性命名策略 JsonNamingPolicy.CamelCase
PropertyNameCaseInsensitive 反序列化大小写不敏感 true
DefaultIgnoreCondition 默认忽略条件 JsonIgnoreCondition.WhenWritingNull
ReferenceHandler 循环引用处理 ReferenceHandler.IgnoreCycles
Converters 自定义转换器集合 new JsonStringEnumConverter()
Encoder 字符编码策略 JavaScriptEncoder.UnsafeRelaxedJsonEscaping
NumberHandling 数字读取策略 JsonNumberHandling.AllowReadingFromString
IncludeFields 包含公共字段 true
TypeInfoResolver 类型元数据解析 源生成 AppJsonContext.Default

3.3 实战代码示例

csharp 复制代码
using System.Text.Json;
using System.Text.Json.Serialization;

public class Order
{
    [JsonPropertyName("order_id")]
    public int Id { get; set; }

    [JsonIgnore]
    public decimal InternalCost { get; set; }   // 该属性不会被序列化

    public string? CustomerName { get; set; }

    [JsonConverter(typeof(JsonStringEnumConverter))]  // 枚举输出为字符串
    public OrderStatus Status { get; set; }
}

public enum OrderStatus
{
    Pending,
    Paid,
    Shipped
}

var options = new JsonSerializerOptions
{
    WriteIndented = true,
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
    PropertyNameCaseInsensitive = true
};

var order = new Order
{
    Id = 1001,
    InternalCost = 30,
    CustomerName = "张三",
    Status = OrderStatus.Paid
};

string json = JsonSerializer.Serialize(order, options);
Console.WriteLine(json);

Order? back = JsonSerializer.Deserialize<Order>(json, options);

输出示例:

json 复制代码
{
  "order_id": 1001,
  "customerName": "张三",
  "status": "Paid"
}

多态序列化示例(.NET 7+):

csharp 复制代码
using System.Text.Json.Serialization;

[JsonPolymorphic(TypeDiscriminatorPropertyName = "$type")]
[JsonDerivedType(typeof(Dog), "dog")]
[JsonDerivedType(typeof(Cat), "cat")]
public abstract class Animal
{
    public string Name { get; set; } = string.Empty;
}

public class Dog : Animal
{
    public bool CanBark { get; set; }
}

public class Cat : Animal
{
    public bool CanMeow { get; set; }
}

4. 安全与最佳实践

实践 说明
默认选 System.Text.Json 内置、高性能、支持源生成和 AOT
禁用 BinaryFormatter 不安全,反序列化不可信数据可能导致远程代码执行;.NET 5+ 已弃用,.NET 9 移除
不反序列化不可信类型 避免类型混淆和恶意构造对象
使用 DTO 控制暴露字段,隔离领域模型和数据库实体
处理循环引用 使用 [JsonIgnore] 或 ReferenceHandler.IgnoreCycles / Preserve
日期时间用 UTC 使用 ISO 8601,避免时区歧义
枚举用字符串 兼容性更好,避免数值变化导致含义错乱
敏感字段排除 使用 [JsonIgnore] 或 DTO 不包含敏感信息
大对象流式处理 使用 SerializeAsync / DeserializeAsync,避免大字符串占内存
版本兼容 新增成员可空或带默认值,反序列化默认忽略未知成员
性能/AOT 使用 JsonSerializerContext 源生成

源生成示例(提升启动性能、支持 AOT):

csharp 复制代码
using System.Text.Json;
using System.Text.Json.Serialization;

[JsonSerializable(typeof(Order))]
internal partial class AppJsonContext : JsonSerializerContext
{
}

string json = JsonSerializer.Serialize(order, AppJsonContext.Default.Order);
Order? back = JsonSerializer.Deserialize(json, AppJsonContext.Default.Order);

异步流式处理(读写大文件,避免一次性占用内存):

csharp 复制代码
await using var stream = File.Create("order.json");
await JsonSerializer.SerializeAsync(stream, order, options);

stream.Position = 0;
Order? back = await JsonSerializer.DeserializeAsync<Order>(stream, options);

5. 小结

  • 序列化是"对象 → 可存储/传输格式",反序列化是其逆过程,是持久化和网络通信的基础;
  • C# 中默认优先使用内置的 System.Text.Json ,老项目或需要高级特性可用 Newtonsoft.Json,XML 场景用 XmlSerializer,高性能跨语言可用 Protobuf;
  • 用特性 (如 [JsonPropertyName]、[JsonIgnore]、[JsonConverter])控制单个成员,用 JsonSerializerOptions 控制命名、缩进、忽略、循环引用等全局行为;
  • 安全上禁用 BinaryFormatter、不反序列化不可信数据、用 DTO 控制暴露字段;大文件用异步流式、追求性能/AOT 用源生成。

掌握"定义模型 → 配置选项/特性 → 序列化与反序列化"这套流程后,日常的接口对接、配置存取、数据缓存都能轻松应对。建议对照本文代码亲手跑一遍,观察不同选项下 JSON 的变化。

相关推荐
JavaGuide2 小时前
NVIDIA 又开源了!这次给 AI Agent 加上权限管控
前端·后端
excel2 小时前
prisma 如何处理数据库竞态
前端·数据库·后端
我命由我123453 小时前
经纪人的职业定义
经验分享·笔记·学习·职场和发展·求职招聘·职场发展·学习方法
2501_942623344 小时前
新能源汽车电子电气架构升级带来的测试挑战
经验分享
知产xiao_xin4 小时前
地理标志权
经验分享·笔记·知识产权
IT_陈寒4 小时前
Java线程池这破玩意,差点让我周末加班排查到凌晨
前端·人工智能·后端
弈栈录5 小时前
Java AI 应用的异步化与高并发设计
java·后端·架构
凤山老林6 小时前
Spring Boot 集成 iText 7 实现动态 PDF 生成与电子签章:合同、报表场景实战
spring boot·后端·pdf·itext7·电子签章
孙启超6 小时前
【AI开发之Rust】第 19 课:UniFFI 导出核心能力
开发语言·后端·rust