【譯】System.Text.Json 的下一步是什麼

MeteorSeed發表於2022-02-11

  .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
  List<int>
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基準)

  在 .NET 5.0 中,為了提高 .NET 在 TechEmpower JSON 基準測試中的效能,我們做了很大的努力。這項工作涉及多個領域,包括網路堆疊、Kestrel 和 JsonSerializer 本身。結果,當觀察在序列化器中完成的工作時,效能現在提高了約19%。
  dotnet/runtime #37976詳細介紹了這些變化和效能度量。這裡有兩組基準。第一個是使用團隊維護的JsonSerializer效能基準測試來驗證效能。觀察到有大約8%的改善。下一部分是 TechEmpower。它衡量了三種不同的方法來滿足 TechEmpower JSON 基準測試的需求。SerializeWithCachedBufferAndWriter 是我們在官方基準測試中使用的。
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
  一旦我們將條目更新到 .NET 5.0,所有跨棧完成的工作將會提高 .NET 在 JSON TechEmpower 基準測試中的位置。我們可以在 .NET 3.1 (第19輪,2020-05-28)和 .NET 5.0 (非正式的持續執行,2020-12-14)中比較 aspcore 的效能:
 

  我們可以看到,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/

 

 

相關文章