.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/