添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

可能有的小伙伴已经知道了,在 .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.Json Json.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 之间差异

  • 受支持功能支持。 从 System.Text.Json 获取类似行为可能需要使用特性或全局选项。
  • 不受支持,可能有解决方法。 解决方法是 自定义转换器 ,它们可能无法提供与 Newtonsoft.Json 功能完全相同的功能。 对于其中一些功能,提供示例代码作为示例。 如果你依赖于这些 Newtonsoft.Json 功能,迁移需要修改 .NET 对象模型或进行其他代码更改。
  • 不受支持,解决方法不可行或无法提供。 如果你依赖于这些 Newtonsoft.Json 功能,则无法在不进行重大更改的情况下进行迁移。 Newtonsoft.Json 功能 | System.Text.Json 等效 | | | --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 默认情况下不区分大小写的反序列化 | ✔️ PropertyNameCaseInsensitive 全局设置 | | | Camel 大小写属性名称 | ✔️ PropertyNamingPolicy 全局设置 | | | 最小字符转义 | ✔️ 严格字符转义,可配置 | | | NullValueHandling.Ignore 全局设置 | ✔️ DefaultIgnoreCondition 全局选项 | 有条件地忽略属性 | | 允许注释 | ✔️ ReadCommentHandling 全局设置 | | | 允许尾随逗号 | ✔️ AllowTrailingCommas 全局设置 | | | 自定义转换器注册 | ✔️ 优先级顺序不同 | | | 默认情况下无最大深度 | ✔️ 默认最大深度为 64,可配置 | | | PreserveReferencesHandling 全局设置 | ✔️ ReferenceHandling 全局设置 | | | 序列化或反序列化带引号的数字 | ✔️ NumberHandling 全局设置,[JsonNumberHandling] 特性 | | | 反序列化为不可变类和结构 | ✔️ JsonConstructor,C# 9 记录 | | | 支持字段 | ✔️ IncludeFields 全局设置,[JsonInclude] 特性 | | | DefaultValueHandling 全局设置 | ✔️ DefaultIgnoreCondition 全局设置 | | | [JsonProperty] 上的 NullValueHandling 设置 | ✔️ JsonIgnore 特性 | | | [JsonProperty] 上的 DefaultValueHandling 设置 | ✔️ JsonIgnore 特性 | | | 反序列化具有非字符串键的 Dictionary | ✔️ 受支持 | | | 支持非公共属性资源库和 Getter | ✔️ JsonInclude 特性 | | | [JsonConstructor] 特性 | ✔️ [JsonConstructor] 特性 | | | 支持范围广泛的类型 | ⚠️ 某些类型需要自定义转换器 | | | 多态序列化 | ⚠️ 不受支持,解决方法,示例 | | | 多态反序列化 | ⚠️ 不受支持,解决方法,示例 | | | 将推断类型反序列化为 object 属性 | ⚠️ 不受支持,解决方法,示例 | | | 将 JSON null 文本反序列化为不可为 null 的值类型 | ⚠️ 不受支持,解决方法,示例 | | | [JsonProperty] 特性上的 Required 设置 | ⚠️ 不受支持,解决方法,示例 | | | DefaultContractResolver 用于忽略属性 | ⚠️ 不受支持,解决方法,示例 | | | DateTimeZoneHandling DateFormatString 设置 | ⚠️ 不受支持,解决方法,示例 | | | 回调 | ⚠️ 不受支持,解决方法,示例 | | | JsonConvert.PopulateObject 方法 | ⚠️ 不受支持,解决方法 | | | ObjectCreationHandling 全局设置 | ⚠️ 不受支持,解决方法 | | | 在不带 setter 的情况下添加到集合 | ⚠️ 不受支持,解决方法 | | | ReferenceLoopHandling 全局设置 | ❌ 不受支持 | | | 支持 System.Runtime.Serialization 特性 | ❌ 不受支持 | | | MissingMemberHandling 全局设置 | ❌ 不受支持 | | | 允许不带引号的属性名称 | ❌ 不受支持 | | | 字符串值前后允许单引号 | ❌ 不受支持 | | | 对字符串属性允许非字符串 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%,内存占用量更小。