小技巧 - System.Text.Json 不轉強型別讀取 JSON
2 |
從 .NET Core 3.0 開始,System.Text.Json 逐步取代 Newtonsoft Json.NET 成為 .NET 處理 JSON 的官方解決方案。在寫 .NET 6 專案時,我也開始嘗試不引用 Json.NET,改以內建 System.Text.Json.JsonSerializer 處理。JsonSerializer.Serializ()/Deserialize<T>() 與 Json.NET JsonConvert 用法相近,較明顯差異是 System.Text.Json 預設會將中文字元轉成 \uxxxx 編碼,需設定 JsonSerializerOptions.Encoder 調整:參考
除了將 JSON 轉成預先定義的物件,Json.NET 有 JObject、JArray,不需轉為強型別即可讀取複雜的 JSON 結構(參考:小技巧 - Json.NET 免定義物件 LINQ 操作),類似需求在 System.Text.Json 則是透過 JsonDocument、JsonElement 實現,這篇將整理相關使用技巧。
假設有 JSON 內容如下:
{
"RS": "Root Property String",
"RI": 123,
"RB": true,
"RD": "2021-12-21T00:00:00Z",
"RA": [
{ "CI": 1, "CT": "A" },
{ "CI": 2, "CT": "B"}
],
"NO": {
"NS": "Neseted Object String",
"NA": [ 1, 2, 3 ],
"NNO": { "NNS": "Nested Nested Object String" }
}
}
原理是以 JsonDocument.Parse() 將 JSON 解析成 DOM,RootElement 為根元素,用 GetProperty("...") 可存取屬性,傳回型別為 JsonElement,我們可透過 GetInt32()、GetString()、GetBoolean()、GetDateTime() 等方法取值;若屬性為子物件,可繼續用 GetProperty() 取子物件屬性,以此類推。JsonElement.EnumerateArray() 可傳回可列舉 JsonElement 集合以 foreach 方式巡覽或使用 LINQ 操作;JsonElement.EnumerateObject() 則可傳回可列舉 JsonProperty 集合,列出物件所有屬性動態處理。若要依型別差異化處理,可由 JsonElement.ValueKind 偵測元素型別,JsonElement.GetRawText() 為原始 JSON 內容。另外,每個 JsonElement 也可 Deserialize<T>() 成指定的強型別物件。
最後來個綜合示範:
using System.Text.Json;
public class Program
{
class RAItem
{
public int CI { get; set; }
public string CT { get; set; }
}
static void Main(string[] args)
{
var json = File.ReadAllText("sample.json");
var jd = JsonDocument.Parse(json);
var root = jd.RootElement;
Console.WriteLine(root.GetProperty("RS").GetString());
if (root.TryGetProperty("RI", out var ri))
Console.WriteLine(ri.GetInt32());
Console.WriteLine(root.GetProperty("RB").GetBoolean());
Console.WriteLine(root.GetProperty("RD").GetDateTime().ToString("yyyy-MM-dd"));
foreach (JsonElement elem in root.GetProperty("RA").EnumerateArray())
{
Console.Write($"CI={elem.GetProperty("CI").GetInt32()}");
//JsonElement 可 Deserialize 轉成物件
var obj = elem.Deserialize<RAItem>();
Console.WriteLine($" CT={obj.CT}");
}
var nestedObject = root.GetProperty("NO");
Console.WriteLine(nestedObject.GetProperty("NS").GetString());
Console.WriteLine(string.Join(", ",
nestedObject.GetProperty("NA").EnumerateArray()
.Select(o => o.GetInt32().ToString()).ToArray()
));
// 直接串接 GetProperty() 深入巢狀物件取得屬性
Console.WriteLine(root.GetProperty("NO").GetProperty("NNO").GetProperty("NNS").GetString());
// 列舉屬性
var props = nestedObject.EnumerateObject();
while (props.MoveNext()) {
var prop = props.Current;
Console.WriteLine($"Prop[{prop.Name}]/{prop.Value.ValueKind}/{prop.Value.GetRawText()}");
}
}
}
執行結果:
Example of using JsonElement to read complex JSON structure in System.Text.Json.
Comments
# by 小海
讚嘆黑大
# by Chris Wong
實用,謝謝