【译】System.Text.Json 的下一步是什么
.NET 5.0 最近发布了,并带来了许多新特性和性能改进。System.Text.Json 也不例外。我们改进了性能和可靠性,并使熟悉 Newtonsoft.Json 的人更容易采用它。在这篇文章中,我将讨论 System.Text.Json 所取得的进展,以及接下来会发生什么。
获取库
-
-
-
如果你的项目是针对 .NET 5 的,安装 .NET 5,System.Text.Json 是开箱即用的,没有任何其他先决条件。
-
如果你的项目目标是 .NET Core 3.x 或 .NET Framework,或者如果你正在编写一个 .NET Standard 兼容库,请安装最新的 System.Text.Json NuGet 包。
-
如果您正在启动一个新的 ASP.NET Core 项目或从 ASP.NET Core 2.2 升级到3.0或5.0,System.Text.Json 是默认的序列化库。
-
-
System.Text.Json 是什么
System.Text.Json 是 .NET 内置的 JSON 序列化库,用于将 .NET 对象类型转换为 JSON 字符串,反之亦然,支持 UTF-8 文本编码。它最初是在 .NET Core 3.0 中添加的。库中的一种流行类型是 JsonSerializer,它为处理 JSON 数据提供最高级的功能。下面是一个简单的例子,如何使用它来序列化和反序列化:
using System; using System.Text.Json; MyType obj = new() { Message = "Hello World!" }; string json = JsonSerializer.Serialize(obj); Console.WriteLine(json); // {"Message":"Hello World!"} MyType anotherObject = JsonSerializer.Deserialize<MyType>("{"Message":"Hello Again."}"); Console.WriteLine(anotherObject.Message); // "Hello Again." public class MyType { public string Message { get; set; } }
为什么是 System.Text.Json ?回顾一下 .NET Core 3.x
处理 JSON 数据已经成为许多 .NET 应用程序的重要组成部分,在许多情况下,这种格式的使用甚至超过了 XML。然而,.NET 并没有一个很好的内建方法来处理 JSON。相反,用户依赖于 Newtonsoft.Json,它继续很好地服务于 .NET 生态系统。很明显,客户将受益于作为框架一部分的现代 JSON库。因此,我们基于以下考虑构建了一个新的JSON库:
-
-
-
提供高性能 JSON api。我们需要一套新的 JSON api,这些 api 的性能经过了高度的调整,利用了框架中的最新特性,比如 Span<T>,并且可以直接处理 UTF-8,而不需要转换成 UTF-16 字符串。这些方面对 ASP.NET Core 至关重要。其中吞吐量是一个关键需求。我们考虑对 Newtonsoft.Json做出一些改变。但我们认为在不破坏现有 Newtonsoft.Json 用户或影响我们所能达到的性能的前提下,这是不可行的。
-
删除 ASP.NET Core 的 Newtonsoft.Json 依赖。在.NET Core 3.x之前,ASP.NET Core 依赖于 Newtonsoft.Json。而这提供了 ASP.NET Core和Newtonsoft.Json之间的紧密集成。这也意味着 Newtonsoft.Json 的版本是由底层平台决定的。然而,Newtonsoft.Json 经常更新,应用程序开发人员经常需要使用特定的版本。因此,我们想要删除 ASP.NET Core 3.0 的 Newtonsoft.Json 依赖。这样客户就可以选择使用哪个版本,而不用担心他们可能会意外地破坏底层平台。
-
-
考虑到在 .NET 生态系统中 Newtonsoft.Json 的普遍性,我们不希望将其作为依赖项从 ASP.NET Core 中移除而不提供简单的方法将其添加回序列化机制。这样做的方法如下:
-
-
-
安装 Microsoft.AspNetCore.Mvc.Newtonsoft.Json 包
-
更新 Startup.ConfigureServices(),调用 AddNewtonsoftJson()
-
-
services.AddMvc()
.AddNewtonsoftJson();
我们在 .NET Core 3.0 中发布了什么
在 NET Core 3.0 中,我们在 System.Text.Json 中提供了以下类型:
-
-
-
JsonSerializer:提供了将 .NET 对象序列化为 JSON 表示以及将 JSON 反序列化为 .NET 对象的功能。
-
JsonDocument:提供随机访问功能,用于检查 JSON 值的结构内容,而无需自动实例化数据值。这种类型是不可变的。
-
JsonElement:表示 JsonDocument 中的特定 JSON 值。
-
Utf8JsonWriter:为 UTF-8 编码的 JSON 文本的只向前、非缓存写入提供高性能 API。
-
Utf8JsonReader:为 UTF-8 编码的 JSON 文本的只向前、按令牌处理(token-by-token)提供高性能 API。
-
-
在 System.Text.Json.Serialization 命名空间中,我们使用 JsonSerializer 为特定于序列化和反序列化的高级场景和定制提供了属性和 API。其中流行的是 JsonConverter<T> 类型,它允许用户控制特定类型、属性或字段的序列化和反序列化。
JsonSerializer 为处理 JSON 数据提供了最高级的功能。由于实现序列化器在持久数据格式和 .NET 对象类型之间进行转换的特性非常广泛,JsonSerializer 正在经历比 JSON 栈中的其他类型更多的开发和接收更多的社区反馈。因此,在这篇文章中,我将主要介绍 JsonSerializer 功能。
在3.0版本中,JsonSerializer 提供了以下功能:
-
-
-
支持序列化和反序列化扁平的旧CLR对象(POCO)、Primitives 和集合
-
内置异步序列化和反序列化
-
UTF-8数据的本地处理
-
不区分大小写的反序列化(以及区分大小写的)
-
使用 JsonNamingPolicy.CamelCase 的 Camel-case 命名策略
-
使用 JsonNamingPolicy 指定自定义命名策略
-
转义的 JSON 数据反序列化
-
序列化支持可选的最小字符转义
-
序列化忽略空值
-
反序列化忽略注释
-
允许尾随逗号
-
自定义转换器
-
使用 [JsonExtensionData] 特性反序列化时捕获额外数据
-
使用 [JsonIgnore] 属性忽略单个属性
-
使用 [JsonProperty] 特性指定自定义属性名
-
-
请参阅《JSON serialization and deserialization (marshalling and unmarshalling) in .NET》,了解 System.Text.Json 的功能概述。
System.Text.Json 与 Newtonsoft.Json 的区别
现在,你可能会问自己,我应该从 Newtonsoft.Json 迁移到 System.Text.Json 吗?正如你可能猜到的那样,答案是:视情况而定。在 .NET Core 3.0 中,System.Text.Json 是 ASP.NET Core 的默认序列化器,因为我们相信它对大多数应用程序来说已经足够好了。但是,如果您通过例如使用 Newtonsoft.Json 的几个特性来大量定制序列化行为,您可能会发现迁移比起只有 POCO 来说更加困难。要了解更多细节,请查看《How to migrate from Newtonsoft.Json to System.Text.Json》。
System.Text.Json 主要关注性能、安全性和符合标准,因为它是新的 .NET 应用程序的默认 JSON 处理堆栈。它在默认行为上有一些关键的不同,并且不打算与 Newtonsoft.Json 具有相同的特性。对于某些场景,使用 System.Text.Json 没有内置的功能,但是有一些推荐的解决方案。对于其他场景,变通方法是不切实际的。如果您的应用程序依赖于某个缺失的特性,请考虑提交一个问题,以确定是否可以添加对您的场景的支持。
这并不是说 System.Text.Json 没有特性或灵活性,或者 Newtonsoft.Json 是缓慢的;这只是为了说明不同的设计哲学,以及当功能与性能或灵活性发生冲突时,我们如何解决。我们对 System.Text.Json 的目标提供了一个快速的内置 JSON 堆栈,它平衡了性能、安全性和特性集。每个特性请求都要仔细权衡这些设计原则。这可能意味着与这些原则相一致的新项目可能更容易使用 System.Text.Json。而旧的项目在迁移过程中可能会遇到更多的障碍。然而, Newtonsoft.Json 并没有错。如果你对它满意,你应该继续使用它。
让我提供两个在可预见的将来我们不打算添加到 System.Text.Json 的特性示例:
-
-
-
通过 System.ComponentModel.TypeConverter 进行类型转换
-
通过 System.Runtime.Serialization 特性调整序列化行为
-
-
它们都被认为是来自旧序列化栈的遗留系统,支持它们会违背 System.Text.Json 的性能优先架构,因为在启动时附加了基于反射的查找,并且在 System.Text.Json.dll 中增加了更多代码的维护和大小负担。此外,任何暗示违背我们的标准遵循原则的特性,例如允许在反序列化时处理格式不正确的 JSON,都不会被接受。
我们在设计 System.Text.Json 时考虑到可扩展性。这意味着即使上述特性在 JsonSerializer 中没有本机支持,它们仍然可以从用户提供的配置中得到支持。例如,用户可以使用自定义的 JsonConverter<T> 来编写对 TypeConverters 调用的包装器。我们也在考虑 IContractResolver 和 JsonProperty-like 的机制(来自 Newtonsoft.Json),以允许编程控制设置类型和成员的元数据和序列化逻辑(dotnet/runtime #31257),这将允许自定义对 System.Runtime.Serialization 特性的支持。
JsonSerializer 支持对 JSON 数据的异步序列化和反序列化作为内置的性能特性。这已经被证明为客户通过编写高可伸缩性、响应性和非阻塞的应用程序提供了重要的价值,Newtonsoft.Json 没有内置的机制来支持异步 JSON 处理。
UTF-8 编码是网络上信息传输的信息的格式。System.Text.Json 与 Newtonsoft.Json 不同,System.Text.Json API 使用这种编码在本地处理数据,不需要来回转码 UTF-16。在将数据作为输入传递到 System.Text.Json API 之前,调用者也不需要手动对数据进行转码。避免这种代码转换还有助于在处理 JSON 数据时产生更好的性能。请注意,基于字符串(UTF-16)的API也可以方便使用,但会带来转码的成本。
性能
在 .NET Core 3.0 的开发周期中,我们在博客中讨论了 System.Text.Json 与 Newtonsoft.Json 的性能比较:
场景 |
速度 |
内存 |
Deserialization |
快2倍 |
相等或更低 |
Serialization |
快1.5倍 |
相等或更低 |
Document (read-only) |
快3-5倍 |
~Allocation free for sizes < 1 MB |
Reader |
快2-3倍 |
~Allocation free (until you materialize values) |
Writer |
快1.3-1.6倍 |
~Allocation free |
与任何性能基准测试一样,场景各不相同,总结数字也有细微差别。这篇文章的摘要是这样的:
主要的目标是性能,我们看到速度能快大约2倍,但这取决于您的场景和负载,所以要确保测量对您来说重要的东西。
有些人认为这是指 System.Text.Json 总是比 Newtonsoft.Json 快一倍,这不是我们想要的。“取决于你的情况和有效载荷”是关键。
在 .NET 生态系统中,JsonSerializer 和其他序列化器之间有过多次性能比较。您可能已经注意到,JsonSerializer 并不总是最快的。当我们说性能是主要目标时,您可能会想为什么会这样。在软件中,几乎没有功能可以在不损害其他功能的情况下最大化,因此所有功能都是各个方面之间的权衡。虽然 JSON 序列化的性能对我们来说非常重要,但这并不是我们唯一的目标。其他目标包括安全性、可靠性、标准遵从性和可用性。
以下是我们在提高性能时所做的一些权衡:
-
-
-
在某些情况下使用类 我们决定将 Utf8JsonWriter 变成一个类。它曾经是一个 ref struct,允许更少的分配和更快的访问我们正在写入的底层。我们将它更改为一个类,以避免开发人员偶然创建写入器的副本(例如,按值将其传递给 helper 方法时),而随后的写入最终覆盖缓冲区中的数据的问题。
-
验证 我们决定在处理 JSON 数据时验证它,这包括 UTF-8 验证和 JSON 语法验证。这对于启用 JSON 输入通常来自不可信来源的服务器和云场景是必要的。
-
扩展点 序列化程序能够注册自定义转换器。这些间接的方法,虽然相对便宜,但在一个狭窄的回路中运行时成本很高;有时即使当它们没有被使用时(由于查找)。
-
-
.NET 5.0有什么新特性?
新特性
在 .NET 5.0 中,我们实现了社区中最受欢迎的一些特性,使熟悉 Newtonsoft.Json 的人更容易使用。以下是新增功能的概述:
GitHub Issue |
描述 |
#30820 |
添加(反)序列化时保存对象引用的机制 |
#32937 |
为 HttpClient 和 HttpContent 添加扩展方法,允许(反)序列化 JSON |
#30255 |
支持(反)序列化带引号的数字 |
#29895 |
支持使用参数化构造函数反序列化对象 |
#876 |
支持(反)序列化字段 |
#779 |
支持忽略值类型的默认值default |
#30687 |
支持有条件地忽略属性(always,never,当 null/default 时) |
#30524 |
支持非字符串字典键 |
#29743 |
允许使用非公共的属性访问器进行(反)序列化 |
#34439 |
为自定义转换器提供处理null的选项 |
#38539 |
支持新的 C# 记录类型 |
#30445 |
向 JsonSerializerOptions 添加 copy 构造函数 |
#34626 |
向接受默认序列化的 JsonSerializerOptions 添加构造函数 |
#31326 |
启用 JsonSerializer 在 Xamarin iOS/Android 上工作 |
当我们宣布 .NET 5.0 和 .NET 5.0 RC 1时,我们介绍了一些新特性,包括改进了对不可变类型的支持,以及在 JSON 对象图中保留引用。在这一节中,我将再讲几个。
支持(反)序列化带引号的数字
当 .NET Core 3.0 发布时,默认情况下不支持将数字类型反序列化为 JSON 字符串。解决方案是为每个适用的数字类型添加一个自定义转换器,它将控制类型的序列化和反序列化,并包含处理带引号的数字的逻辑。在 .NET 5 中,我们添加了一个方便的可选特性,以支持序列化和反序列化带引号的数字和命名的浮点字面值(NaN、Infinity 和 -Infinity)。下面是一个使用该特性的示例:
using System; using System.Text.Json; using System.Text.Json.Serialization; var options = new JsonSerializerOptions { NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString }; string json = @"{""NumberOne"":1,""NumberTwo"":""2""}"; ClassWithInts @class = JsonSerializer.Deserialize<ClassWithInts>(json, options); Console.WriteLine(@class.NumberOne); // 1 Console.WriteLine(@class.NumberTwo); // 2 json = JsonSerializer.Serialize(@class, options); Console.WriteLine(json); // @"{""NumberOne"":""1"",""NumberTwo"":""2""}"; public class ClassWithInts { public int NumberOne { get; set; } public int NumberTwo { get; set; } }
除了全局应用的 JsonSerializerOptions.NumberHandling 选项之外,我们还添加了 JsonNumberHandlingAttribute,它允许指定类型、属性或字段的数字处理设置。
支持忽略值类型的default值
在 .NET Core 3.x 中序列化时,它只能忽略引用的 null 值或可为空值类型的属性,以及 JsonSerializerOptions.IgnoreNullValues 设置仅适用于整个输入对象图。在 .NET 5 中,我们增加了对不可空值类型(如 int 和 float)忽略默认值的支持。此外,现在可以选择默认时忽略的属性和字段。示例如下:
var options = new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault }; var obj = new MyClass(); string json = JsonSerializer.Serialize(obj, options); Console.WriteLine(json); // {"MyBool":false} public class MyClass { public int MyInt { get; set; } [JsonIgnore(Condition = JsonIgnoreCondition.Never)] public bool MyBool { get; set; } public string MyString { get; set; } }
这里我们看到了新特性的组合:我们告诉序列化器忽略整个对象图的默认值,但指出无论全局选项如何,都不应该忽略其中一个属性。要只忽略空值,而不忽略值类型的默认值,请使用 JsonIgnoreCondition.WhenWritingNull。
通过这些处理默认值的扩展,JsonSerializerOptions.IgnoreNullValues 属性在即将发布的 .NET 6.0 版本中已经过时。在反序列化和序列化时都执行 IgnoreNullValues 是不需要的,因为没有好理由在输入的有效负载中忽略 null 符号。
JsonSerializerOptions 的实用构造器
将 JsonSerializerOptions 设置从一个实例复制到另一个实例,然后进行一些更改,这是一种常见的操作。在 .NET 5 中,我们为 JsonSerializerOptions 添加了一个复制构造函数,这使得这个过程更加简单:
JsonSerializerOptions options = new() { NumberHandling = JsonNumberHandling.AllowReadingFromString, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, Converters = { new JsonStringEnumConverter() } }; JsonSerializerOptions newOptions = new(options) { NumberHandling = JsonNumberHandling.Strict }; Console.WriteLine(newOptions.NumberHandling); // Strict Console.WriteLine(newOptions.DefaultIgnoreCondition); // WhenWritingNull Console.WriteLine(newOptions.Converters[0]); // System.Text.Json.Serialization.JsonStringEnumConverter
在 web 上下文中处理 JSON 数据时,一组常见的选项是使用驼峰命名策略,在反序列化时指定不区分大小写的匹配,并允许读取带引号的数字。新 JsonSerializerDefaults.Web enum 值,以及一个接受 JsonSerializerDefaults 值的新构造函数,提供了一种简单的方法,以一致的方式跨应用程序的多个层重用这些选项(例如,Blazor 场景的客户端、共享和服务器)。让我们一起来看看:
JsonSerializerOptions options = new(JsonSerializerDefaults.Web); Console.WriteLine(options.PropertyNamingPolicy); // System.Text.Json.JsonCamelCaseNamingPolicy Console.WriteLine(options.PropertyNameCaseInsensitive); // True Console.WriteLine(options.NumberHandling); // AllowReadingFromString
有关 JsonSerializer 支持哪些特性的更多信息,请参阅迁移指南中的该表。
性能改善
在 .NET Core 3.1 和 .NET 5 之间,我们在以下方面改进了性能:
-
-
-
改进了集合的序列化和反序列化性能
-
改进了小型类型的序列化和反序列化性能
-
改进了不区分大小写和缺少属性的情况下的反序列化性能
-
改进了长 JSON 字符串的序列化性能
-
-
这些改进对高性能应用程序尤其有意义。
改进了集合的序列化和反序列化性能
我们对大型集合进行了显著的改进(反序列化时约1.15x-1.5x,序列化时约1.5x-2.4x)。你可以在 dotnet/runtime #2259 中更详细地看到这些改进。下面的数字显示了处理包含1024个元素的集合的性能数字。
Dictionary<string, string>
Method
|
Mean
|
Error
|
StdDev
|
Median
|
Min
|
Max
|
Gen 0
|
Gen 1
|
Gen 2
|
Allocated
|
Deserialize Before
|
190.4 us
|
1.47 us
|
1.38 us
|
190.6 us
|
188.5 us
|
193.6 us
|
26.5554
|
8.3460
|
–
|
163.69 KB
|
After ~1.2x faster
|
158.8 us
|
1.27 us
|
1.13 us
|
158.8 us
|
157.3 us
|
161.3 us
|
26.5991
|
8.8664
|
–
|
164.05 KB
|
Serialize Before
|
109.7 us
|
0.77 us
|
0.72 us
|
109.5 us
|
108.5 us
|
111.1 us
|
3.4904
|
–
|
–
|
23.92 KB
|
After ~1.5x faster
|
74.53 us
|
0.590 us
|
0.552 us
|
74.40 us
|
73.57 us
|
75.69 us
|
3.8179
|
0.2937
|
–
|
24.17 KB
|
Method
|
Mean
|
Error
|
StdDev
|
Median
|
Min
|
Max
|
Gen 0
|
Gen 1
|
Gen 2
|
Allocated
|
Deserialize Before
|
76.40 us
|
0.392 us
|
0.366 us
|
76.37 us
|
75.53 us
|
76.87 us
|
1.2169
|
–
|
–
|
8.25 KB
|
After ~1.5x faster
|
50.05 us
|
0.251 us
|
0.235 us
|
49.94 us
|
49.76 us
|
50.43 us
|
1.3922
|
–
|
–
|
8.62 KB
|
Serialize Before
|
29.04 us
|
0.213 us
|
0.189 us
|
29.00 us
|
28.70 us
|
29.34 us
|
1.2620
|
–
|
–
|
8.07 KB
|
After ~2.4x faster
|
12.17 us
|
0.205 us
|
0.191 us
|
12.15 us
|
11.97 us
|
12.55 us
|
1.3187
|
–
|
–
|
8.34 KB
|
改进了小型类型的性能(TechEmpower基准)
Method
|
Mean
|
Error
|
StdDev
|
Median
|
Min
|
Max
|
Gen 0
|
Gen 1
|
Gen 2
|
Allocated
|
SerializeWithCachedBufferAndWriter (Before)
|
155.3 ns
|
1.19 ns
|
1.11 ns
|
155.5 ns
|
153.3 ns
|
157.3 ns
|
0.0038
|
–
|
–
|
24 B
|
SerializeWithCachedBufferAndWriter (After) ~1.19x faster
|
130.8 ns
|
1.50 ns
|
1.40 ns
|
130.9 ns
|
128.6 ns
|
133.0 ns
|
0.0037
|
–
|
–
|
24 B
|
我们可以看到,JSON 序列化的每秒响应数(RPS)从904,846增加到1,190,245,提高了约31%。
改进了不区分大小写和 extra-property 的反序列化性能
使用 JSON 最常见的一个问题是命名约定与 .NET 设计准则不匹配。JSON 属性通常是 camelCase,.NET 属性和字段通常是 PascalCase。您使用的 JSON 序列化器负责在命名约定之间建立桥梁。这不是免费的,至少在 .NET Core 3.1 中不是。在 .NET 5.0 中,这个成本可以忽略不计。
允许不区分大小写和额外属性的代码在 .NET 5.0 中得到了极大的改进。在某些情况下,它要快1.75倍。
下面的基准测试是一个简单的4属性测试类,它的属性名超过7个字符。
3.1 性能
Method |
Mean |
Error |
StdDev |
Median |
Min |
Max |
Gen 0 |
Gen 1 |
Gen 2 |
Allocated |
CaseSensitive_Matching |
844.2 ns |
4.25 ns |
3.55 ns |
844.2 ns |
838.6 ns |
850.6 ns |
0.0342 |
– |
– |
224 B |
CaseInsensitive_Matching |
833.3 ns |
3.84 ns |
3.40 ns |
832.6 ns |
829.4 ns |
841.1 ns |
0.0504 |
– |
– |
328 B |
CaseSensitive_NotMatching (Extra) |
1,007.7 ns |
9.40 ns |
8.79 ns |
1,005.1 ns |
997.3 ns |
1,023.3 ns |
0.0722 |
– |
– |
464 B |
CaseInsensitive_NotMatching |
1,405.6 ns |
8.35 ns |
7.40 ns |
1,405.1 ns |
1,397.1 ns |
1,423.6 ns |
0.0626 |
– |
– |
408 B |
5.0 性能
Method |
Mean |
Error |
StdDev |
Median |
Min |
Max |
Gen 0 |
Gen 1 |
Gen 2 |
Allocated |
CaseSensitive_Matching |
799.2 ns |
4.59 ns |
4.29 ns |
801.0 ns |
790.5 ns |
803.9 ns |
0.0985 |
– |
– |
632 B |
CaseInsensitive_Matching |
789.2 ns |
6.62 ns |
5.53 ns |
790.3 ns |
776.0 ns |
794.4 ns |
0.1004 |
– |
– |
632 B |
CaseSensitive_NotMatching (Extra) |
479.9 ns |
0.75 ns |
0.59 ns |
479.8 ns |
479.1 ns |
481.0 ns |
0.0059 |
– |
– |
40 B |
CaseInsensitive_NotMatching |
783.5 ns |
3.26 ns |
2.89 ns |
783.5 ns |
779.0 ns |
789.2 ns |
0.1004 |
– |
– |
632 B |
提高长 JSON 字符串的序列化性能
dotnet/ corefx# 41845 利用 SSE2 指令来进行安全检查,看看一个 JSON 字符串是否需要转义,速度更快。当序列化常见的有效负载时,有~10-20%的改进。此外,当直接使用写入器写入相对较大的 JSON 字符串时,会有~30%的改进。这个 GitHub 的要点将详细讨论这个更改的性能特征。一个有趣的数据点是,当序列化表示 NuGet 搜索结果的 POCO 实例时,一个基准测试显示了改进:
Method |
Mean |
Error |
StdDev |
Median |
Min |
Max |
Gen 0 |
Gen 1 |
Gen 2 |
Allocated |
SerializeNugetPayload (Before) |
791.7 ms |
15.69 ms |
16.12 ms |
787.4 ms |
772.0 ms |
827.1 ms |
– |
– |
– |
787.3 MB |
SerializeNugetPayload (After) ~1.13x faster |
698.4 ms |
6.63 ms |
5.53 ms |
699.5 ms |
690.9 ms |
708.4 ms |
– |
– |
– |
787.3 MB |
破坏性的变化
我们在 .NET Core 3.x 到 .NET 5.0 之间做了一些行为上破坏性的改变。鉴于 System.Text.Json 是一个平台组件,遵循一个严格的兼容性标准,就像 .NET 的其他组件一样,这些更改是基于对其影响的仔细评估而做出的,并服务于整个库的改进。尽管这些变化很重要,但它们通常都是针对边缘情况的,尤其是一致性方面,并且对使用库的大多数人的影响应该是最小的。对于更重要的行为改变和新特性的添加,我们让他们选择加入,以避免破坏针对库的以前版本编写的现有代码。检索以下文档查看详细信息:
-
-
-
《Deserialization of char types requires a single-character string》
-
《ASP.NET Core apps allow deserializing quoted numbers》
-
《JsonSerializer.Serialize throws ArgumentNullException when Type parameter is null》
-
《Non-public, parameterless constructors not used for deserialization》
-
《PropertyNamingPolicy, PropertyNameCaseInsensitive, and Encoder options are honored when serializing and deserializing KeyValuePair<TKey,TValue>》
-
-
System.Text.Json 的下一步是什么?
我们已经开始规划 .NET 6.0。我们将继续处理帮助驱动 System.Text.Json 的最需要的特性。在更多的 .NET 应用程序中,System.Text.Json 是一个可行的 JSON 堆栈选择,权衡每个请求和我们的设计原则。请参阅 dotnet/runtime 43620,了解该建议的概述。下面是一些顶级功能的简介。
用于 JSON 序列化的 C# 源代码生成器(dotnet/runtime #1568)
投资的一个领域是利用新的 C# 源代码生成器特性来生成代码,这些代码可以在以下方面帮助序列化器:
-
-
-
提高了启动性能
-
改善运行时吞吐量
-
减少私有字节的使用
-
ILLinker 由于避免运行时反射而带来的友好性
-
通过促进 linker 删除序列化器中未使用的基于反射的代码路径和未使用的转换器,从而减少应用程序的大小
-
-
这项工作正在进行中,并处于原型阶段。这项工作的行动项目和进展可以通过 dotnet/runtimelab 的 JSON Code Gen 项目来观察。
更新后的支持源代码生成的 System.Text.JsonJson 可以通过一个实验性的 NuGet 包来使用。请与我们分享您在使用此功能时观察到的任何性能变化。问题可以在这里用 area-JsonCodeGen 标签记录。
扩展多态的序列化和反序列化(dotnet/runtime #45189)
多态序列化和反序列化仍然是现代 .NET 应用程序的重要场景。我们希望在 .NET 6.0 中实现的特性可以支持这些场景。
dynamic 和可变的 JSON DOM (dotnet/runtime #45188)
支持序列化和反序列化 dynamic 类型以及提供可变 JSON 文档是两个密切相关且重要的特性,它们将消除许多客户的阻塞。我们将这些作为 .NET 6.0 潜在的工作项进行跟踪。
杂项改进(dotnet/runtime #45190)
在 .NET 6.0 中,我们想要做很多其他的特性和改进。一些功能对于 System.Text.Json 将是创新的和独特的。例如支持异步序列化和反序列化的 IAsyncEnumerable<T> 实例(dotnet /运行时# 1570)。其他特性可能更加熟悉,比如添加 snake_case 支持(dotnet/runtime #782),以及能够更改默认的 JsonSerializerOptions 设置(dotnet/runtime #31094)。实现这些特性将会增加 System.Text.Json 的适用性。
写在最后
.NET 5.0 是 System.Text.Json 的一个重要版本。如果你的项目不是针对 .NET 5.0,你仍然可以通过安装最新的 System.Text.Json NuGet 包来使用。
我们在 .NET 5.0 中做的很多工作都是由社区驱动的。@YohDeadfall 实现了字段支持,并为 JsonSerializer 提供了各种优化。@NikiforovAll 实现了 JsonSerializerOptions 构造函数,该构造函数接受 JsonSerializerDefaults 值。@marcusturewicz 实现了对 JsonDocument 实例的序列化和反序列化的支持。@devsko 在我们发布之前对各种问题做出了修复。@CodeBlanch 修复了 null 和 nullability 问题。@Marusyk 修复了新的 preserve-references 特性中引用相等的错误。@khellang 修复了读取时验证 DateTime 和 DateTimeOffset 有效载荷的错误。@alanisaac, @thomaslevesque, @marcusturewicz, @madmir, @NikiforovAll, @JoshSchreuder, @Jacksondr5 和@KimKiHyuk 贡献了改进 System.Text.Json 测试覆盖率的变更,使其持续接近100%。我们不能在这里强调所有的贡献,但是 .NET 感谢页面向所有的 .NET 运行时贡献者表示了敬意。
在 .NET 6.0 中,我们将继续进行更多的改进。随着我们的进步, System.Text.Json 将非常受欢迎。只需访问 GitHub 就能查询其现状。如果你有勇气,可以看看那些没有“up-for-grabs”标签的发行。和往常一样,我们非常欢迎反馈,特别是现在我们正在计划下一个版本时。
原文链接
https://devblogs.microsoft.com/dotnet/whats-next-for-system-text-json/
作者:MeteorSeed
我希望您喜欢这篇博文,并一如既往地感谢您阅读并与朋友和同事分享我的博文。
转载请注明出处。