先前介紹 ASP.NET Core 2.2 約略提過 .NET Core 3.0 將移除對 Json.NET 的依賴,改成可抽換式允許使用者更換偏好的版本。

前幾天微軟開發部落格有篇文章:Try the new System.Text.Json APIs!,詳細介紹 .NET Team 取代 Json.NET 的解決方案 - System.Text.Json。

.NET Core 3.0 決定擺脫對 Json.NET 依賴,內建 System.Text.Json 做為預設 JSON 處理核心。 與 Newtonsoft Json.NET 相比,System.Text.Json API 主打以下特點:

  • 強調效能(主要是啟用了 .NET Core 的祕密武器 - Span<T> )
  • 低記憶體耗用量
  • 支援 Native UTF-8 (Json.NET 會將 UTF-8 轉成 Unicode String 處理,多了轉換程序,耗用記憶體也多)

.NET Team 棄用 Json.NET 的幾項考量:

  1. Json.NET 必須兼顧原有平台,封殺了引進 .NET Core 特有 Span<T> 提升效能的可能性。(擺脫此一限制後,System.Text.Json 的確能加速 1.3 至 5 倍)
  2. Json.NET 改版頻率高,ASP.NET Core 有時需鎖定特定版本,開發者升級 Json.NET 版本後出錯時有所聞,移除依賴後,開發者可任意選用偏好的 Json.NET 版本。
  3. Json.NET 與 ASP.NET Core 整合環節被抽取成 NuGet 套件,讓開發者可以沿用 Json.NET,也可抽換其他 JSON 程式庫。

Newtonsoft Json.NET 歷史悠久,功能強大且齊全,要取代並非易事,.NET Core BCL 開發團隊採 MVP (Minimum Viable Prodcut,最小可行產品)策略, 先實作最基本的序列化、反序列化、文件查詢、低階 Reader/Writer... 等必要功能,未來再視社群反應逐步擴充。

由於 System.Text.Json 功能還不齊全,遇到不支援的功能,開發者可設定(IServiceColletion.AddNewtonJson())回頭使用 Json.NET, BCL Team 計劃依開發者需求調整開發優先順序(開發社群可透過 GitHub Issue 回饋),有個例子是 AllowTrailingCommas,允許陣列最後一筆有多餘的「,」,雖不合法但很多現有資料就是這副德行,於是它成為選項。

為單次小量讀取,定義強列別物件再反序列化並不划算,JsonDocument 允許用 XML 觀念讀取 JSON 內容,用最精簡資源完成工作: (類似情境在 Json.NET 可用 JObject + dynamic 解決,參考:小技巧 - JSON 免定義物件 LINQ 操作, 但我喜歡 JsonDocument 的做法,更直覺單純。)

using (JsonDocument doc = JsonDocument.Parse(json, new JsonReaderOptions { AllowTrailingCommas = true })
{
    JsonElement root = document.RootElement;
    JsonElement students = root.GetProperty("Students");
    foreach (JsonElement student in students.EnumerateArray()) 
    {
        if (student.TryGetProperty("Grade", out JsonElement grade)
        {
            sum += grade.GetDouble();
        }
    }
}

JsonDocument 被設計成唯讀,未存取到的部分不轉成物件或陣列,不配置記憶體,讀取大型物件的效能大幅提升。

JSON Writer/Reader 範例:

using (var stream = new MemoryStream()) 
{
    using (var writer = new Utf8JsonWriter(stream))
    {
        writer.WriteStartObject();
        writer.WriteString("date", DateTimeOffset.Now);
        writer.WriteNumber("temp", 42);
        writer.WriteEndObject();
    }
}

var reader = new Utf8JsonReader(byteArray, isFinalBlock: treu, state: dafault);
while (reader.Read())
{
    Console.Write(reader.TokenType);
    switch (reader.TokenType)
    {
        case JsonTokenType.PropertyName:
        case JsonTokenType.String:
            Console.Write(" " + reader.GetString());
            break;
        //... 
    }
}

System.Text.Json 提供 .NET Standard 版本,.NET Framework 4.6+ 可透過 NuGet 安裝使用,但少了 Span<T> 加持,效能不如 .NET Core 版。

效能方面,System.Text.Json 在處理速度及耗用記憶體都明顯勝過 Json.NET,是採用它最主要的誘因:

讀到這裡,有同學舉手發問,Json.NET 歷史悠久,功能齊全,又有超強大的客製彈性,System.Text.Json 能超越它嗎?

我認為會!

理由很簡單 - Json.NET 的作者 James Newton-King,已加入微軟,勢必也會參與 System.Text.Json 開發,精彩可期。

而我們該如何選擇 Json.NET 或 System.Text.Json? 我想沒人比 James 本人更有資格發表看法了:

Json.NET was created over 10 years ago, and since then it has added a wide range of features aimed to help developers work with JSON in .NET. In that time Json.NET has also become far and away NuGet's most depended on and downloaded package, and is the go-to library for JSON support in .NET. Unfortunately, Json.NET's wealth of features and popularity works against making major changes to it. Supporting new technologies like Span<T> would require fundamental breaking changes to the library and would disrupt existing applications and libraries that depend on it.
Going forward Json.NET will continue to be worked on and invested in, both addressing known issues today and supporting new platforms in the future. Json.NET has always existed alongside other JSON libraries for .NET, and there will be nothing to prevent you using one or more together, depending on whether you need the performance of the new JSON APIs or the large feature set of Json.NET.

歷經十年的發展,Json.NET 發展出各式功能以滿足開發者要求,長年位居 NuGet 套件下載之冠,而其受歡迎的程度及流通之廣也成了莫大包袱。引進 Span<T> 將無可避免破壞相容性,危及依賴它的眾多應用程式(所以才改由 System.Text.Json 扛起突破效能瓶頸的重任)。Json.NET 仍會繼續開發、修正問題並支援新平台,永遠是眾多 JSON 解決方案之一,依據效能優先或是功能擴充彈性重要,開發者可自由選擇甚至同時使用多個合適的解決方案。

簡言之,寫 .NET Core 3.0+ 程式,若是基本 JSON 轉換及查詢需求,享受 System.Text.Json 的迅速輕巧為上策;若有特殊花式 JSON 處理需求,Json.NET 仍是不二選擇。

Study of System.Text.Json.


Comments

# by Martin Wang

第一個示例有 typo 啦: JsonDocuemnt -> JsonDocument

# by Jeffrey

to Martin Wang, 感謝指正。

Post a comment