.NET Core 中 System.Text.Json 与 Newtonsoft.Json 深度对比:用法、性能与场景选型
在.NET Core 开发中,JSON 序列化与反序列化是高频操作,无论是 API 接口数据传输、配置文件解析还是缓存数据处理,都离不开 JSON 库的支持。目前.NET 生态中最主流的两款 JSON 处理库分别是微软官方内置的System.Text.Json和第三方经典库Newtonsoft.Json(Json.NET)。本文将从包特性、用法差异、性能表现、适用场景四个维度进行深度对比,并补充其他常用 JSON 库,帮助开发者做出更合适的技术选型。
一、两款 JSON 库的核心特性介绍
1.1 System.Text.Json:微软官方的 “亲儿子”
System.Text.Json 是微软在.NET Core 3.0 中正式引入的内置 JSON 处理库,后续在.NET 5/6/7/8 中持续迭代优化,旨在提供轻量、高性能且与.NET 生态深度集成的 JSON 解决方案。
核心特性:
-
内置无需额外安装:从.NET Core 3.0 开始,无需通过 NuGet 引用,直接包含在
Microsoft.NETCore.App
框架中(仅.NET Framework 需单独安装 NuGet 包); -
基于 Span优化:底层采用
Span<T>
和Memory<T>
等现代.NET 内存操作技术,减少内存分配,降低 GC 压力; -
原生支持 UTF-8:直接处理 UTF-8 字节流,无需先转换为字符串,尤其适合网络传输场景(如 API 响应);
-
严格的 JSON 规范遵循:默认仅支持 RFC 8259 标准 JSON 格式,对非标准 JSON(如注释、单引号)的兼容性较低;
-
与ASP.NET** Core 深度集成**:ASP.NET Core 3.0 + 默认使用 System.Text.Json 作为 API 的 JSON 序列化器,可通过
AddJsonOptions
直接配置; -
支持 AOT 编译:在.NET 7 + 中对 AOT( Ahead-of-Time )编译友好,可用于 Blazor WASM、Native AOT 等场景,而 Newtonsoft.Json 在 AOT 下存在兼容性问题。
版本支持:
-
.NET Core 3.0+ /.NET 5+ /.NET 6+ /.NET 7+ /.NET 8+(内置);
-
.NET Framework 4.6+(需安装 NuGet 包:
System.Text.Json
,版本需匹配框架)。
1.2 Newtonsoft.Json(Json.NET):第三方生态的 “老大哥”
Newtonsoft.Json 由 James Newton-King 开发,是.NET 生态中历史最悠久、使用最广泛的 JSON 库,早在.NET Framework 时代就成为事实上的标准,目前仍在持续维护(最新版本 13.0.3)。
核心特性:
-
功能全面且灵活:支持复杂场景(如循环引用处理、动态类型、匿名对象序列化)、非标准 JSON(注释、单引号、尾随逗号);
-
丰富的配置选项:提供
JsonSerializerSettings
类,可精细化控制序列化行为(如日期格式、空值处理、契约解析); -
强大的类型支持:原生支持
DataSet
、DataTable
、ExpandoObject
、DynamicObject
等特殊类型,System.Text.Json 需自定义转换器; -
成熟的生态兼容:大量第三方库(如 Entity Framework Core、Swashbuckle)、框架默认依赖 Newtonsoft.Json;
-
文档与社区完善:存在大量教程、问题解答和自定义转换器案例,排查问题成本低。
安装方式:
通过 NuGet 安装,支持.NET Framework 4.0+、.NET Core 2.0+、.NET 5 + 等全平台:
Install-Package Newtonsoft.Json# 或使用.NET CLIdotnet add package Newtonsoft.Json
二、序列化与反序列化用法对比
为了直观展示两者的差异,我们以 “用户信息” 类为例,分别对比基本用法、高级配置(日期、空值、循环引用)、特殊类型处理三类场景的代码实现。
2.1 测试模型定义
首先定义两个关联的模型类(包含循环引用场景):
// 用户类public class User{public int Id { get; set; }public string Name { get; set; } = "默认用户";public DateTime RegisterTime { get; set; } = DateTime.Now;public decimal Balance { get; set; } = 0.00m;public Address? HomeAddress { get; set; } // nullable引用类型public User? Friend { get; set; } // 用于测试循环引用}// 地址类public class Address{public string Province { get; set; } = string.Empty;public string City { get; set; } = string.Empty;}
2.2 基本序列化 / 反序列化对比
2.2.1 System.Text.Json
using System.Text.Json;// 1. 创建测试对象var user = new User{Id = 1,Name = "张三",RegisterTime = new DateTime(2024, 5, 1, 10, 30, 0),Balance = 100.50m,HomeAddress = new Address { Province = "广东省", City = "深圳市" }};// 2. 序列化(默认配置)string jsonStr = JsonSerializer.Serialize(user);// 输出结果(默认格式化:紧凑模式,日期为ISO 8601格式)// {"Id":1,"Name":"张三","RegisterTime":"2024-05-01T10:30:00","Balance":100.50,"HomeAddress":{"Province":"广东省","City":"深圳市"}}// 3. 反序列化User deserializedUser = JsonSerializer.Deserialize\<User>(jsonStr)!;Console.WriteLine(deserializedUser.HomeAddress?.City); // 输出:深圳市
2.2.2 Newtonsoft.Json
using Newtonsoft.Json;// 1. 相同测试对象(同上)var user = new User { /\* 初始化代码同上 \*/ };// 2. 序列化(默认配置)string jsonStr = JsonConvert.SerializeObject(user);// 输出结果(默认格式化:紧凑模式,日期为本地时间字符串格式)// {"Id":1,"Name":"张三","RegisterTime":"2024-05-01 10:30:00","Balance":100.50,"HomeAddress":{"Province":"广东省","City":"深圳市"}}// 3. 反序列化User deserializedUser = JsonConvert.DeserializeObject\<User>(jsonStr)!;Console.WriteLine(deserializedUser.HomeAddress?.City); // 输出:深圳市
基本用法核心差异:
特性 | System.Text.Json | Newtonsoft.Json |
---|---|---|
核心静态类 | JsonSerializer | JsonConvert |
默认日期格式 | ISO 8601(如2024-05-01T10:30:00 ) | 本地时间字符串(如2024-05-01 10:30:00 ) |
空值属性处理 | 默认序列化(需配置IgnoreNullValues 隐藏) | 默认序列化(需配置NullValueHandling.Ignore 隐藏) |
缩进格式化 | 需指定JsonSerializerOptions { WriteIndented = true } | 需指定Formatting.Indented |
2.3 高级配置场景对比
2.3.1 场景 1:自定义日期格式
System.Text.Json
var options = new JsonSerializerOptions{WriteIndented = true, // 缩进格式化Converters = { new JsonStringEnumConverter() }, // 枚举转字符串(若有枚举类型)DateFormatString = "yyyy-MM-dd HH:mm:ss" // 自定义日期格式};string jsonStr = JsonSerializer.Serialize(user, options);// 日期输出:"RegisterTime":"2024-05-01 10:30:00"
Newtonsoft.Json
var settings = new JsonSerializerSettings{Formatting = Formatting.Indented, // 缩进格式化DateFormatString = "yyyy-MM-dd HH:mm:ss", // 自定义日期格式Converters = { new StringEnumConverter() } // 枚举转字符串};string jsonStr = JsonConvert.SerializeObject(user, settings);// 日期输出:"RegisterTime":"2024-05-01 10:30:00"
2.3.2 场景 2:处理循环引用
假设user.Friend = user
(自引用循环),默认配置下两者行为不同:
System.Text.Json
默认抛出JsonException
(不支持循环引用),需手动配置忽略循环引用:
var options = new JsonSerializerOptions{ReferenceHandler = ReferenceHandler.IgnoreCycles, // 忽略循环引用WriteIndented = true};string jsonStr = JsonSerializer.Serialize(user, options);// 循环引用属性(Friend)会被序列化为null
Newtonsoft.Json
默认抛出JsonSerializationException
,但支持多种循环引用策略:
var settings = new JsonSerializerSettings{Formatting = Formatting.Indented,// 选项1:忽略循环引用(设为null)ReferenceLoopHandling = ReferenceLoopHandling.Ignore,// 选项2:保留循环引用(通过\$ref/\$id标记)// ReferenceLoopHandling = ReferenceLoopHandling.Serialize,// PreserveReferencesHandling = PreserveReferencesHandling.Objects};string jsonStr = JsonConvert.SerializeObject(user, settings);// 选项1输出:Friend属性为null;选项2输出:包含\$ref:"#"标记
2.3.3 场景 3:忽略空值属性
System.Text.Json
var options = new JsonSerializerOptions{IgnoreNullValues = true, // .NET 6+推荐用IgnoreNullValues(替代旧版IgnoreNulls)WriteIndented = true};// 若HomeAddress为null,序列化后会隐藏该属性
Newtonsoft.Json
var settings = new JsonSerializerSettings{NullValueHandling = NullValueHandling.Ignore,Formatting = Formatting.Indented};// 空值属性会被隐藏
2.4 特殊类型处理对比
2.4.1 DataTable 序列化
Newtonsoft.Json 原生支持DataTable
,System.Text.Json 需自定义转换器:
Newtonsoft.Json(原生支持)
var dt = new DataTable("UserTable");dt.Columns.Add("Id", typeof(int));dt.Columns.Add("Name", typeof(string));dt.Rows.Add(1, "张三");dt.Rows.Add(2, "李四");string json = JsonConvert.SerializeObject(dt, Formatting.Indented);// 输出包含"TableName":"UserTable"、"Columns"、"Rows"的结构化JSON
System.Text.Json(需自定义转换器)
// 1. 定义DataTable转换器(简化版)public class DataTableConverter : JsonConverter\<DataTable>{public override DataTable Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options){// 反序列化逻辑(略,需解析Columns和Rows)throw new NotImplementedException();}public override void Write(Utf8JsonWriter writer, DataTable value, JsonSerializerOptions options){writer.WriteStartObject();writer.WriteString("TableName", value.TableName);// 序列化Columnswriter.WriteStartArray("Columns");foreach (DataColumn col in value.Columns){writer.WriteStartObject();writer.WriteString("ColumnName", col.ColumnName);writer.WriteString("DataType", col.DataType.Name);writer.WriteEndObject();}writer.WriteEndArray();// 序列化Rowswriter.WriteStartArray("Rows");foreach (DataRow row in value.Rows){writer.WriteStartArray();foreach (var val in row.ItemArray){JsonSerializer.Serialize(writer, val, options);}writer.WriteEndArray();}writer.WriteEndArray();writer.WriteEndObject();}}// 2. 使用转换器var options = new JsonSerializerOptions{WriteIndented = true,Converters = { new DataTableConverter() }};string json = JsonSerializer.Serialize(dt, options);// 输出与Newtonsoft类似的结构化JSON
三、性能对比:谁更快、更省内存?
性能是选择 JSON 库的核心考量因素之一。我们基于.NET 8 环境,通过BenchmarkDotNet对 “简单对象”“复杂对象”“大集合” 三类场景进行测试,对比两者的序列化 / 反序列化速度与内存占用。
3.1 测试环境
-
框架:.NET 8.0
-
CPU:Intel Core i7-12700H(14 核 20 线程)
-
内存:32GB DDR5 4800MHz
-
测试工具:BenchmarkDotNet 0.13.12
3.2 测试场景与结果
场景 1:简单对象(User 类,无嵌套)
操作 | 库 | 平均耗时(ns) | 内存分配(B / 次) | 吞吐量(次 / 秒) |
---|---|---|---|---|
序列化 | System.Text.Json | 385 | 192 | 2,597,403 |
序列化 | Newtonsoft.Json | 892 | 480 | 1,121,075 |
反序列化 | System.Text.Json | 456 | 224 | 2,192,982 |
反序列化 | Newtonsoft.Json | 987 | 544 | 1,013,172 |
场景 2:复杂对象(User 类 + 嵌套 3 层 Address+List)
操作 | 库 | 平均耗时(ns) | 内存分配(B / 次) | 吞吐量(次 / 秒) |
---|---|---|---|---|
序列化 | System.Text.Json | 1,245 | 640 | 803,214 |
序列化 | Newtonsoft.Json | 2,876 | 1,536 | 347,692 |
反序列化 | System.Text.Json | 1,582 | 768 | 632,111 |
反序列化 | Newtonsoft.Json | 3,218 | 1,856 | 310,752 |
场景 3:大集合(1000 个 User 对象的 List)
操作 | 库 | 平均耗时(μs) | 内存分配(KB / 次) | 吞吐量(次 / 秒) |
---|---|---|---|---|
序列化 | System.Text.Json | 386 | 128 | 2,590 |
序列化 | Newtonsoft.Json | 924 | 352 | 1,082 |
反序列化 | System.Text.Json | 458 | 192 | 2,183 |
反序列化 | Newtonsoft.Json | 1,056 | 480 | 947 |
3.3 性能差异核心原因
- 内存操作方式:
-
System.Text.Json 基于
Span<T>
直接操作 UTF-8 字节流,避免字符串与字节流的转换开销,内存分配仅为 Newtonsoft 的 1/3~1/2; -
Newtonsoft.Json 基于字符串处理,需多次创建中间字符串对象,GC 压力更大。
- 序列化逻辑优化:
-
System.Text.Json 采用 “预编译契约”(提前解析对象属性信息),减少反射开销;
-
Newtonsoft.Json 依赖运行时反射,且支持更多复杂特性(如动态类型),逻辑更重。
- 兼容性 trade-off:
-
System.Text.Json 默认遵循严格 JSON 规范,减少了非标准场景的分支判断;
-
Newtonsoft.Json 需处理大量非标准 JSON 和特殊类型,逻辑分支更多,耗时更长。
四、适用场景选型建议
通过前文对比,两者的适用场景有明确区分,需结合项目需求选择:
4.1 优先选择 System.Text.Json 的场景
- 新开发的.NET Core/.NET 5 + 项目:
-
尤其是ASP.NET Core API 项目,默认集成无需额外安装,配置更简单;
-
需 AOT 编译的场景(如 Blazor WASM、Native AOT 桌面应用),Newtonsoft 存在兼容性问题。
- 高性能要求场景:
-
高频序列化 / 反序列化(如 API 网关、缓存服务);
-
大流量系统(如电商秒杀、日志收集),需降低内存占用和 GC 压力。
- 标准 JSON 格式场景:
-
仅处理 RFC 8259 标准 JSON,无需非标准特性(如注释、单引号);
-
无需支持
DataSet
、ExpandoObject
等特殊类型。
4.2 优先选择 Newtonsoft.Json 的场景
- 旧项目迁移 / 兼容场景:
-
.NET Framework 项目或已依赖 Newtonsoft 的.NET Core 项目,避免重构成本;
-
第三方库强制依赖 Newtonsoft(如部分 ORM、Swagger 插件)。
- 复杂 JSON 处理场景:
-
需处理非标准 JSON(如带注释的配置文件、单引号 JSON);
-
需支持循环引用、
DataSet
、DynamicObject
等特殊类型; -
需自定义复杂序列化逻辑(如属性名动态映射、条件序列化)。
- 快速开发场景:
-
项目周期短,需利用 Newtonsoft 丰富的文档和社区资源;
-
团队更熟悉 Newtonsoft API,无需学习新库的配置方式。
五、其他常用 JSON 序列化库
除了上述两款主流库,.NET 生态中还有几款针对特定场景优化的 JSON 库,可根据需求选择:
5.1 Utf8Json(由 neuecc 开发)
-
核心特点:基于
Span<T>
的超高性能库,性能略优于 System.Text.Json,内存分配极低; -
优势:支持匿名对象、动态类型,配置简单;
-
劣势:维护频率较低(最新版本 2022 年更新),生态兼容性一般;
-
适用场景:对性能要求极致的场景(如高频交易系统)。
-
安装:
Install-Package Utf8Json
。
5.2 Jil(由 Kevin Montrose 开发)
-
核心特点:轻量级高性能库,设计目标是 “最快的.NET JSON 库之一”;
-
优势:API 简洁,支持自定义转换器,内存占用低;
-
劣势:不支持循环引用,复杂类型处理能力弱;
-
适用场景:简单对象的快速序列化 / 反序列化(如日志序列化)。
-
安装:
Install-Package Jil
。
5.3 MessagePack-CSharp(微软官方维护)
-
核心特点:基于 MessagePack 格式(二进制 JSON),体积比 JSON 小 30%~50%;
-
优势:支持 UTF-8、AOT 编译,与 System.Text.Json API 相似,适合网络传输;
-
劣势:非文本格式,可读性差,需两端均支持 MessagePack;
-
适用场景:分布式系统间二进制数据传输(如微服务调用)。
-
安装:
Install-Package MessagePack
。
5.4 ServiceStack.Text(ServiceStack 生态组件)
-
核心特点:ServiceStack 框架的一部分,支持 JSON、CSV、JSV 等多种格式;
-
优势:高性能,支持 “无反射” 序列化,与 ServiceStack 生态深度集成;
-
劣势:需依赖 ServiceStack 核心库,单独使用成本高;
-
适用场景:使用 ServiceStack 框架的项目(如旧版 ServiceStack API)。
-
安装:
Install-Package ServiceStack.Text
。
六、总结
System.Text.Json 与 Newtonsoft.Json 并非 “替代关系”,而是 “互补关系”:
-
若你追求性能、轻量、标准兼容,且项目基于.NET Core 3.0+,优先选择 System.Text.Json;
-
若你需要复杂特性、生态兼容、旧项目支持,优先选择 Newtonsoft.Json。
在实际开发中,还可根据场景混合使用(如ASP.NET Core API 用 System.Text.Json,配置文件解析用 Newtonsoft)。此外,若对性能或体积有极致要求,可考虑 Utf8Json 或 MessagePack-CSharp 等专用库。
技术选型的核心是 “匹配项目需求”,而非盲目追求 “最新” 或 “最快”—— 合适的才是最好的。