.NET Core 開始內建 System.Text.Json,強調效能快、省記憶體,計劃逐步取代 Json.NET。從 .NET Core 3 一路發展,System.Text.Json 加入 JsonObject、JsonArray 支援 DOM 巡覽及 JSON 文件編修,功能及順手度慢慢追上 Json.NET。但就實務面,Json.NET 有大量現成範例、程式庫滿足五花八門需求,為求統一硬要全面改寫成 System.Text.Json 並非明智之舉。於是,專案混用 System.Text.Json 與 Json.NET 的案例還蠻常見的。

最近遇到一例,ASP.NET Core Minimal API 整合現有程式庫,方法回傳類型為 JObject,我沒想太多,就用 Results.Json() 打算將其轉為 JSON 傳回前端:

using System.Text.Json;
using Newtonsoft.Json.Linq;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => Results.Json(someLegacyApi()));

app.Run();

JObject someLegacyApi() => new JObject
{
    ["cpu"] = "i7",
    ["os"] = new JObject
    {
        ["ver"] = "Windows 11",
        ["lang"] = "en-US",
    },
    ["drives"] = new JArray
    {
        "SSD 1TB", "HDD 2TB"        
    }
};

預期該傳回 {"cpu":"i7","os":{"ver":"Windows 11","lang":"en-US"},"drives":["SSD 1TB","HDD 2TB"]},結果得到 {"cpu":[],"os":[[[]],[[]]],"drives":[[],[]]} 這種鬼東西:

我很快明白,JObject 是 Json.NET 特製的類別,無法像一般類別用 Reflection GetProperties() 列舉屬性,Json.NET 是自己加上訂製邏輯才能順利序列化。而要解這個問題不難,先將 JObject 轉成 JSON 字串,再反序列化還原成 System.Text.Json 世界的類別,之後即可暢行無阻。

試了兩種寫法:JsonSerializer.Deserialize<JsonElement>()JsonDocument.Parse(),都能順利解決問題:

app.MapGet("/jsonelem", () => Results.Json(
    JsonSerializer.Deserialize<JsonElement>(someLegacyApi().ToString())));
app.MapGet("/jsondoc", () => Results.Json(
    JsonDocument.Parse(someLegacyApi().ToString())));

Tips of how to serialize JObject with System.Text.Json.


Comments

Be the first to post a comment

Post a comment