Text.Json和Newtonsoft.Json(Json.Net)对比

3,745 阅读10分钟

前言

可能有的小伙伴已经知道了,在.NET Core 3.0中微软加入了对JSON的内置支持。 一直以来.NET开发者们已经习惯使用Json.NET这个强大的库来处理JSON。 那么.NET为什么要增加JSON的内置支持呢? 最近,.NET的官方博客再次发表文章说明了这么做的原因、并介绍了相关API的用法。原文地址: https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-apis/。那么,现在就结合自身的理解来分析一下Text.JsonJson.NET的区别。

System.Text.Json介绍

System.Text.Json 命名空间提供用于序列化和反序列化 JavaScript 对象表示法 (JSON) 的功能。 System.Text.Json 库包含在 .NET Core 3.1 和更高版本的运行时中。 对于其他目标框架,请安装 System.Text.Json NuGet 包。 包支持以下框架:

  • .NET Standard 2.0 及更高版本
  • .NET Framework 4.7.2 及更高版本
  • .NET Core 2.0、2.1 和 2.2 System.Text.Json 主要关注性能、安全性和标准符合性。 它在默认行为方面有一些重要差异,不打算具有与 Newtonsoft.Json 相同的功能。 对于某些方案,System.Text.Json 没有内置功能,但有建议解决方法。

Newtonsoft.Json 与 System.Text.Json 之间差异

默认 JsonSerializer 行为相较于 Newtonsoft.Json 的差异

System.Text.Json 在默认情况下十分严格,避免代表调用方进行任何猜测或解释,强调确定性行为。 该库是为了实现性能和安全性而特意这样设计的。 Newtonsoft.Json 默认情况下十分灵活。 设计中的这种根本差异是默认行为中以下许多特定差异的背后原因。

不区分大小写的反序列化

在反序列化过程中,默认情况下 Newtonsoft.Json 进行不区分大小写的属性名称匹配。 System.Text.Json 默认值区分大小写,这可提供更好的性能,因为它执行精确匹配。 有关如何执行不区分大小写的匹配的信息,请参阅不区分大小写的属性匹配

如果使用 ASP.NET Core 间接使用 System.Text.Json,则无需执行任何操作即可获得类似于 Newtonsoft.Json 的行为。 ASP.NET Core 在使用 System.Text.Json 时,会为 camel 大小写属性名称和不区分大小写的匹配指定设置。

默认情况下,ASP.NET Core 还允许反序列化带引号的数字

最小字符转义

在序列化过程中,Newtonsoft.Json 对于让字符通过而不进行转义相对宽松。 也就是说,它不会将它们替换为 \uxxxx(其中 xxxx 是字符的码位)。 对字符进行转义时,它会通过在字符前发出 `` 来实现此目的(例如," 会变为 ")。 System.Text.Json 会在默认情况下转义较多字符,以对跨站点脚本 (XSS) 或信息泄露攻击提供深度防御保护,并使用六字符序列执行此操作。 System.Text.Json 会在默认情况下转义所有非 ASCII 字符,因此如果在 Newtonsoft.Json 中使用 StringEscapeHandling.EscapeNonAscii,则无需执行任何操作。 System.Text.Json 在默认情况下还会转义 HTML 敏感字符。 有关如何替代默认 System.Text.Json 行为的信息,请参阅自定义字符编码

注释

在反序列化过程中,Newtonsoft.Json 在默认情况下会忽略 JSON 中的注释。 System.Text.Json 默认值是对注释引发异常,因为 RFC 8259 规范不包含它们。 有关如何允许注释的信息,请参阅允许注释和尾随逗号

尾随逗号

在反序列化过程中,默认情况下 Newtonsoft.Json 会忽略尾随逗号。 它还会忽略多个尾随逗号(例如 [{"Color":"Red"},{"Color":"Green"},,])。 System.Text.Json 默认值是对尾随逗号引发异常,因为 RFC 8259 规范不允许使用它们。 有关如何使 System.Text.Json 接受它们的信息,请参阅允许注释和尾随逗号。 无法允许多个尾随逗号。

转换器注册优先级

自定义转换器的 Newtonsoft.Json 注册优先级如下所示:

  • 属性上的特性
  • 类型上的特性
  • 转换器 集合

此顺序意味着 Converters 集合中的自定义转换器会由通过在类型级别应用特性而注册的转换器替代。 这两个注册都会由属性级别的特性替代。

自定义转换器的 System.Text.Json 注册优先级是不同的:

  • 属性上的特性
  • Converters 集合
  • 类型上的特性

此处的差别在于 Converters 集合中的自定义转换器会替代类型级别的特性。 此优先级顺序的目的是使运行时更改替代设计时选项。 无法更改优先级。

有关自定义转换器注册的详细信息,请参阅注册自定义转换器

最大深度

Newtonsoft.Json 默认情况下没有最大深度限制。 对于 System.Text.Json,默认限制为 64,可通过设置 JsonSerializerOptions.MaxDepth 进行配置。

如果使用 ASP.NET Core 时间接使用 System.Text.Json,则默认的最大深度限制为 32。 默认值与模型绑定的默认值相同,并且在 JsonOptions 类中设置。

JSON 字符串(属性名称和字符串值)

在反序列化过程中,Newtonsoft.Json 接受用双引号、单引号括起来或不带引号的属性名称。 它接受用双引号或单引号括起来的字符串值。 例如,Newtonsoft.Json 接受以下 JSON:

{
  "name1": "value",
  'name2': "value",
  name3: 'value'
}

System.Text.Json 仅接受双引号中的属性名称和字符串值,因为 RFC 8259 规范要求使用该格式,这是唯一视为有效 JSON 的格式。

用单引号括起来的值会导致 JsonException,并出现以下消息:

''' is an invalid start of a value.

字符串属性的非字符串值

Newtonsoft.Json 接受非字符串值(如数字或文本 true 和 false),以便反序列化为类型字符串的属性。 下面是 Newtonsoft.Json 成功反序列化为以下类的 JSON 示例:

{
  "String1": 1,
  "String2": true,
  "String3": false
}
public class ExampleClass
{
    public string String1 { get; set; }
    public string String2 { get; set; }
    public string String3 { get; set; }
}

System.Text.Json 不将非字符串值反序列化为字符串属性。 字符串字段接收的非字符串值会导致 JsonException,并出现以下消息:

The JSON value could not be converted to System.String.

使用 JsonSerializer 的方案

下面一部分方案不受内置功能支持,但有解决方法可用。 解决方法是自定义转换器,它们可能无法提供与 Newtonsoft.Json 功能完全相同的功能。 对于其中一些功能,提供示例代码作为示例。 如果你依赖于这些 Newtonsoft.Json 功能,迁移需要修改 .NET 对象模型或进行其他代码更改。

对于下面的一部分方案,解决方法不可行或无法提供。 如果你依赖于这些 Newtonsoft.Json 功能,则无法在不进行重大更改的情况下进行迁移。具体参考:## 使用 JsonSerializer 的方案

性能

由于此功能受性能的强烈推动,我们希望分享新API的一些高级性能特征。

请记住,这些都是基于预览版本,最终数字很可能会有所不同。我们还在调整会影响性能的默认行为(例如,区分大小写)。请注意,这些都是微基准测试。您的里程肯定会有所不同,因此如果性能对您至关重要,请确保针对最能代表您工作负载的方案进行自己的测量。

原生 System.Text.Json

只需进行微基准测试即可 System.Text.Json 与Json.NET 进行比较 ,得出以下结果:

场景速度内存
反序列化快2倍持平或更低
序列化快1.5倍持平或更低
文件(只读)快3-5倍<1 MB无分配
读取器快2-3倍无分配(直到实现值(materialize values))
写入器快1.3-1.6倍无分配

ASP.NET Core MVC 中的 System.Text.Json

我们编写了一个ASP.NET Core应用程序,可以动态生成 数据 ,然后从MVC控制器进行序列化和反序列化 。然后我们改变有效载荷大小并测量结果:

JSON反序列化(输入)

描述吞吐量(RPS)CPU (%)内存 (MB)
Newtonsoft.Json – 500 B136,43595172
System.Text.Json – 500 B167,86194169
Newtonsoft.Json – 2.4 KB97,13797174
System.Text.Json – 2.4 KB32,02696169
Newtonsoft.Json – 40 KB7,71288212
System.Text.Json – 40 KB16,62596193

JSON序列化(输出)

描述吞吐量(RPS)CPU(%)内存(MB)
Newtonsoft.Json - 500 B120,27394174
System.Text.Json - 500 B145,63194173
Newtonsoft.Json - 8 KB35,40898187
System.Text.Json - 8 KB56,42497184
Newtonsoft.Json - 40 KB8,41699202
System.Text.Json - 40 KB14,84898197

对于最常见的有效负载大小, System.Text.Json 在输入和输出格式化期间,MVC的吞吐量增加约20%,内存占用量更小。