.NET Core 3.0 以降で利用できる JsonSerializer クラスの Deserialize<TValue>(String, JsonSerializerOptions) メソッドを使って JSON 文字列を JsonElement 構造体のオブジェクトにデシリアライズし、それをパースして下の画像のように表示する方法を書きます。
基本的には先の記事「Json.NET の JToken をパース」と同じことを行っています。違うのは先の記事では JToken クラスにデシリアライズしていたものが JsonElement 構造体になったところです。
それに伴い、先の記事では JToken が JObject, JArray, JValue のどれにキャストできるかによってオブジェクトなのか配列なのかプリミティブ値なのかを調べてそれぞれ処理を分けていたところが、この記事の JsonElement の場合は JsonElement.ValueKind プロパティを使って JsonValueKind 列挙型のどれに該当するかを調べて処理を分けることになります。
また、foreach ループでの反復処理を、Object が対象の場合は JsonElement.EnumerateObject メソッドを使って、Array が対象の場合は JsonElement.EnumerateArray メソッドを使って列挙子を取得して行うところも先の記事とは異なります。
そのあたりは以下のサンプルコードのコメントに書きましたので見てください。
まず、Deserialize<JsonElement>(jsonText) で JSON 文字列を JsonElement 型のオブジェクトにデシリアライズします。その後 Parse メソッドで JsonElement オブジェクトの中身がプリミティブ型なのか Object なのか Array なのかを再帰的に解析し結果をコンソールに出力したのが上の画像です。
using System;
using System.Text.Json;
using System.Text.Encodings.Web;
using System.Text.Unicode;
using System.IO;
namespace ConsoleAppJson
{
class Program
{
static void Main(string[] args)
{
string path = @"C:\Users\...\ConsoleAppJson\";
string file = "TextFile5.txt";
string jsonText = "";
using (StreamReader sr = File.OpenText(path + file))
{
jsonText = sr.ReadToEnd();
}
var jelem = JsonSerializer.Deserialize<JsonElement>(jsonText);
Parse(0, jelem);
}
private static void Parse(int padding, JsonElement jelem)
{
// JsonElement.ValueKind プロパティを使って JsonValueKind 列挙型
// のどれに該当するかを調べて処理を分ける
if (jelem.ValueKind == JsonValueKind.False ||
jelem.ValueKind == JsonValueKind.Null ||
jelem.ValueKind == JsonValueKind.Number ||
jelem.ValueKind == JsonValueKind.String ||
jelem.ValueKind == JsonValueKind.True)
{
string str = $"value = {jelem}";
Console.WriteLine(str.PadLeft(str.Length + padding));
}
else if (jelem.ValueKind == JsonValueKind.Object)
{
// Object の場合は EnumerateObject() で列挙子を取得し、以下の
// ように foreach ループで JsonProperty を取得できる。
// JsonProperty というのは単一の {"name":"value"} オブジェクト
// と思えばよさそう
foreach (JsonProperty jprop in jelem.EnumerateObject())
{
if (jprop.Value.ValueKind == JsonValueKind.False ||
jprop.Value.ValueKind == JsonValueKind.Null ||
jprop.Value.ValueKind == JsonValueKind.Number ||
jprop.Value.ValueKind == JsonValueKind.String ||
jprop.Value.ValueKind == JsonValueKind.True)
{
string str = $"name = {jprop.Name}, value = {jprop.Value}";
Console.WriteLine(str.PadLeft(str.Length + padding));
}
else if (jprop.Value.ValueKind == JsonValueKind.Object)
{
string str = $"name = {jprop.Name}";
Console.WriteLine(str.PadLeft(str.Length + padding));
Parse(padding + 2, jprop.Value);
}
else if (jprop.Value.ValueKind == JsonValueKind.Array)
{
string str = $"name = {jprop.Name}";
Console.WriteLine(str.PadLeft(str.Length + padding));
int index = 1;
// Array の場合は EnumerateArray() で列挙子を取得し、以下のよう
// に foreach ループで配列内の各要素 (JsonElement) を取得できる
foreach (JsonElement jelemInArray in jprop.Value.EnumerateArray())
{
string idx = $"array index {index}";
Console.WriteLine(idx.PadLeft(idx.Length + padding + 1));
Parse(padding + 2, jelemInArray);
index++;
}
}
else
{
// JsonValueKind.Undefined 以外はここに来ない(はず)
Console.WriteLine(jelem.ToString());
}
}
}
else if (jelem.ValueKind == JsonValueKind.Array)
{
int index = 1;
// Array の場合 EnumerateArray() で列挙子を取得し、以下のよう
// に foreach ループで配列内の各要素 (JsonElement) を取得できる
foreach (JsonElement jelemInArray in jelem.EnumerateArray())
{
string idx = $"array index {index}";
Console.WriteLine(idx.PadLeft(idx.Length + padding + 1));
Parse(padding + 2, jelemInArray);
index++;
}
}
else
{
// JsonValueKind.Undefined 以外はここに来ない(はず)
Console.WriteLine(jelem.ToString());
}
}
}
}
上の画像を出力するのに使った JSON 文字列は以下の通りです。
{
"Title": "This is my title",
"Response": {
"Version": 1,
"StatusCode": "OK",
"Result": {
"Profile": {
"UserName": "SampleUser",
"IsMobileNumberVerified": false,
"MobilePhoneNumber": null
},
"ObjectArray" : [
{"Code": 2000,"Description": "Fail"},
{"Code": 3000,"Description": "Success"}
],
"lstEnrollment": "2021-2-5"
},
"Message": {
"Code": 1000,
"Description": "OK"
}
},
"StringArray" : ["abc", "def", "ghi"]
}