.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类,可精细化控制序列化行为(如日期格式、空值处理、契约解析);

  • 强大的类型支持:原生支持DataSetDataTableExpandoObjectDynamicObject等特殊类型,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 CLI
dotnet 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\(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\(jsonStr)!;
Console.WriteLine(deserializedUser.HomeAddress?.City); // 输出:深圳市
基本用法核心差异:
特性System.Text.JsonNewtonsoft.Json
核心静态类JsonSerializerJsonConvert
默认日期格式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\
{
   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);
       // 序列化Columns
       writer.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();
       // 序列化Rows
       writer.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.Json3851922,597,403
序列化Newtonsoft.Json8924801,121,075
反序列化System.Text.Json4562242,192,982
反序列化Newtonsoft.Json9875441,013,172
场景 2:复杂对象(User 类 + 嵌套 3 层 Address+List)
操作平均耗时(ns)内存分配(B / 次)吞吐量(次 / 秒)
序列化System.Text.Json1,245640803,214
序列化Newtonsoft.Json2,8761,536347,692
反序列化System.Text.Json1,582768632,111
反序列化Newtonsoft.Json3,2181,856310,752
场景 3:大集合(1000 个 User 对象的 List)
操作平均耗时(μs)内存分配(KB / 次)吞吐量(次 / 秒)
序列化System.Text.Json3861282,590
序列化Newtonsoft.Json9243521,082
反序列化System.Text.Json4581922,183
反序列化Newtonsoft.Json1,056480947

3.3 性能差异核心原因

  1. 内存操作方式
  • System.Text.Json 基于Span<T>直接操作 UTF-8 字节流,避免字符串与字节流的转换开销,内存分配仅为 Newtonsoft 的 1/3~1/2;

  • Newtonsoft.Json 基于字符串处理,需多次创建中间字符串对象,GC 压力更大。

  1. 序列化逻辑优化
  • System.Text.Json 采用 “预编译契约”(提前解析对象属性信息),减少反射开销;

  • Newtonsoft.Json 依赖运行时反射,且支持更多复杂特性(如动态类型),逻辑更重。

  1. 兼容性 trade-off
  • System.Text.Json 默认遵循严格 JSON 规范,减少了非标准场景的分支判断;

  • Newtonsoft.Json 需处理大量非标准 JSON 和特殊类型,逻辑分支更多,耗时更长。

四、适用场景选型建议

通过前文对比,两者的适用场景有明确区分,需结合项目需求选择:

4.1 优先选择 System.Text.Json 的场景

  1. 新开发的.NET Core/.NET 5 + 项目
  • 尤其是ASP.NET Core API 项目,默认集成无需额外安装,配置更简单;

  • 需 AOT 编译的场景(如 Blazor WASM、Native AOT 桌面应用),Newtonsoft 存在兼容性问题。

  1. 高性能要求场景
  • 高频序列化 / 反序列化(如 API 网关、缓存服务);

  • 大流量系统(如电商秒杀、日志收集),需降低内存占用和 GC 压力。

  1. 标准 JSON 格式场景
  • 仅处理 RFC 8259 标准 JSON,无需非标准特性(如注释、单引号);

  • 无需支持DataSetExpandoObject等特殊类型。

4.2 优先选择 Newtonsoft.Json 的场景

  1. 旧项目迁移 / 兼容场景
  • .NET Framework 项目或已依赖 Newtonsoft 的.NET Core 项目,避免重构成本;

  • 第三方库强制依赖 Newtonsoft(如部分 ORM、Swagger 插件)。

  1. 复杂 JSON 处理场景
  • 需处理非标准 JSON(如带注释的配置文件、单引号 JSON);

  • 需支持循环引用、DataSetDynamicObject等特殊类型;

  • 需自定义复杂序列化逻辑(如属性名动态映射、条件序列化)。

  1. 快速开发场景
  • 项目周期短,需利用 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 等专用库。

技术选型的核心是 “匹配项目需求”,而非盲目追求 “最新” 或 “最快”—— 合适的才是最好的。

posted on 2025-10-12 20:24  ljbguanli  阅读(31)  评论(0)    收藏  举报