C#序列化:从 Newtonsoft.Json 迁移到 System.Text.Json
引言
在.NET生态中,JSON序列化一直是数据交互的核心环节。早期,Newtonsoft.Json凭借其灵活性和丰富的功能成为开发者的首选。然而,随着.NET Core的发展,微软推出了内置的System.Text.Json库,其性能和安全性优势逐渐凸显。特别是在.NET 8中,System.Text.Json进一步优化,成为更高效、更可靠的选择。本文将结合微软官方文档和实际开发经验,详细介绍如何从Newtonsoft.Json迁移到System.Text.Json,并解决迁移过程中常见的问题。
一、为什么选择System.Text.Json?
1. 性能优势
System.Text.Json在序列化和反序列化速度上显著优于Newtonsoft.Json,尤其在处理大规模数据时表现突出。例如,在.NET 8中,通过编译时生成序列化代码和内存布局优化,其性能提升可达2-3倍。这对于高并发的Web API或数据密集型应用至关重要。
2. 内置支持与安全性
作为.NET Core和.NET 5+的原生库,System.Text.Json无需额外安装依赖,与框架深度集成。其默认行为严格遵循JSON标准,避免了Newtonsoft.Json的宽松解析可能带来的安全漏洞,例如禁止尾随逗号、严格转义特殊字符等。
3. 现代化特性
.NET 8的System.Text.Json引入了JSON Schema导出、流式处理多个文档、可空引用类型支持等新功能,满足现代开发需求。例如,使用JsonSchemaExporter
可以自动生成JSON Schema,简化API文档管理。
二、迁移步骤详解
1. 替换依赖包
- 移除Newtonsoft.Json:在项目中卸载
Newtonsoft.Json
NuGet包。 - 添加System.Text.Json:安装
System.Text.Json
包,确保项目引用.NET 8或更高版本。
2. 代码替换
- 基础序列化/反序列化
// Newtonsoft.Json
string json = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
var result = Newtonsoft.Json.JsonConvert.DeserializeObject<MyType>(json);
// System.Text.Json
string json = System.Text.Json.JsonSerializer.Serialize(obj);
var result = System.Text.Json.JsonSerializer.Deserialize<MyType>(json);
- 配置选项迁移
// Newtonsoft.Json
var settings = new Newtonsoft.Json.JsonSerializerSettings
{
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver(),
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore
};
// System.Text.Json
var options = new System.Text.Json.JsonSerializerOptions
{
PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
};
3. 处理常见差异
命名策略
System.Text.Json默认使用PascalCase命名,需显式设置PropertyNamingPolicy
为CamelCase
以匹配前端需求。若需忽略大小写,可设置PropertyNameCaseInsensitive = true
。字符转义
System.Text.Json默认转义非ASCII字符,可通过Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
关闭。例如:
var options = new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
循环引用处理
Newtonsoft.Json默认抛出异常,需配置ReferenceLoopHandling = ReferenceLoopHandling.Ignore
。System.Text.Json则需通过ReferenceHandler.Preserve
或自定义解析器处理。日期格式
Newtonsoft.Json默认使用ISO 8601格式,而System.Text.Json需显式配置:
options.DateTimeFormat = "yyyy-MM-dd";
或使用自定义转换器。
三、高级场景处理
1. 自定义转换器
- 日期处理
public class CustomDateTimeConverter : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return DateTime.ParseExact(reader.GetString()!, "yyyy-MM-dd", CultureInfo.InvariantCulture);
}
public override void Write(Utf8JsonWriter writer, DateTime dateTimeValue, JsonSerializerOptions options)
{
writer.WriteStringValue(dateTimeValue.ToString("yyyy-MM-dd"));
}
}
注册转换器:
options.Converters.Add(new CustomDateTimeConverter());
- 枚举处理
默认序列化为整数,需转为字符串:
options.Converters.Add(new JsonStringEnumConverter());
或通过特性指定。
2. 多态序列化
- 使用特性
[JsonPolymorphic(TypeDiscriminatorPropertyName = "Type")]
[JsonDerivedType(typeof(Student), "Student")]
[JsonDerivedType(typeof(Teacher), "Teacher")]
public abstract class Person { /* ... */ }
- 自定义类型解析器
public class PolymorphicTypeResolver : DefaultJsonTypeInfoResolver
{
public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options)
{
var jsonTypeInfo = base.GetTypeInfo(type, options);
if (type == typeof(Person))
{
jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions
{
TypeDiscriminatorPropertyName = "Type",
DerivedTypes = { new JsonDerivedType(typeof(Student), "Student") }
};
}
return jsonTypeInfo;
}
}
配置选项:
options.TypeInfoResolver = new PolymorphicTypeResolver();
3. 框架兼容性
- ASP.NET Core
在Program.cs
中配置:
builder.Services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
});
- 第三方库集成
某些框架(如Bot SDK)可能仍依赖Newtonsoft.Json,需手动替换配置。例如,修改AddNewtonsoftJson()
为AddJsonOptions()
。
四、性能优化技巧
1. 复用JsonSerializerOptions
避免重复创建配置对象,提高性能:
private static readonly JsonSerializerOptions Options = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
2. 使用源生成器
在.NET 6+中,通过[JsonSerializable]
特性生成无反射的序列化代码:
[JsonSerializable(typeof(Product))]
internal partial class AppJsonContext : JsonSerializerContext {}
使用:
var json = JsonSerializer.Serialize(product, AppJsonContext.Default.Product);
3. 流式处理
处理大文件时,使用异步流式API减少内存占用:
await using var stream = File.Create("data.json");
await JsonSerializer.SerializeAsync(stream, largeObject);
五、常见问题与解决方案
1. 反序列化失败
- 原因:属性名不匹配、类型不兼容等。
- 解决:启用
PropertyNameCaseInsensitive
,或使用JsonPropertyName
特性标注。
2. 不支持DataTable
- 原因:System.Text.Json不直接支持复杂类型如DataTable。
- 解决:转换为列表或使用扩展方法。
3. 动态对象处理
- 方案:使用
JsonElement
或自定义包装类,存储类型信息以支持反序列化。
六、总结
迁移到System.Text.Json不仅能提升性能和安全性,还能更好地利用.NET框架的新特性。虽然过程中需要处理配置差异和兼容性问题,但通过合理的代码调整和工具辅助(如.NET升级助手),可以顺利完成迁移。对于新项目,推荐直接使用System.Text.Json;对于现有项目,逐步替换关键模块并通过单元测试验证兼容性是可行的策略。未来,随着.NET的持续优化,System.Text.Json将成为JSON序列化的首选方案。
通过以上步骤,开发者可以高效地完成从Newtonsoft.Json到System.Text.Json的迁移,充分享受.NET 8带来的性能与功能优势。