WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

System.Text.Json の JsonElement をパース (CORE)

by WebSurfer 2021年2月8日 13:03

.NET Core 3.0 以降で利用できる JsonSerializer クラスDeserialize<TValue>(String, JsonSerializerOptions) メソッドを使って JSON 文字列を JsonElement 構造体のオブジェクトにデシリアライズし、それをパースして下の画像のように表示する方法を書きます。

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"]
}

Tags: , , , ,

CORE

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2021年2月  >>
31123456
78910111213
14151617181920
21222324252627
28123456
78910111213

View posts in large calendar