Json.NET (Newtonsoft.Json) の JsonConvert.DeserializeObject メソッドを使って JSON 文字列を JToken 型のオブジェクトにデシリアライズし、それを以下のようにパースして表示する方法を書きます。
使用するのは Newtonsoft.Json.Linq 名前空間にある JToken, JObject, JArray, JValue クラスですので、まずそれらの簡単な説明を以下に書きます。詳しい説明は Newtonsoft.Json.Linq Namespace を見てください。
-
JToken: 下のクラスの継承元の抽象クラス
-
JObject: {"name1":"value1","name2":"value2"} のようなオブジェクト。IDictionary<string, JToken> を継承
-
JArray: [{"name":"value1"},{"name":"value2"}, ...] のような配列。IList<JToken> を継承
-
JValue: string, number, boolean, null などのプリミティブな JSON 値
一般的な方法は DeserializeObject<T>(String) メソッドの T に先の記事「JSON 文字列から C# のクラス定義生成」のような方法で求めたクラス定義を設定して、そのクラスのオブジェクトにデシリアライズするのだと思います。
なので、この記事に書くような JToken クラスにデシリアライズしてそれをパースする必要はなさそうですが、ひょっとしたら JSON 文字列を受け取るまでその内容が不明でクラス定義が生成できないとかいうケースがあるかもしれないということで考えてみました。
そのコードを以下にアップしておきます。
DeserializeObject<JToken>(jsonText) で JSON 文字列を JToken 型のオブジェクトにデシリアライズします。その後 Parse メソッドで JToken オブジェクトを再帰的に解析し結果をコンソールに出力したのが上の画像です。
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.IO;
namespace NewtonsoftJson
{
class Program
{
static void Main(string[] args)
{
string path = @"C:\Users\...\NewtonsoftJson\";
string file = "TextFile5.txt";
string jsonText = "";
file = "TextFile5.txt";
using (StreamReader sr = File.OpenText(path + file))
{
jsonText = sr.ReadToEnd();
}
JToken jtoken = JsonConvert.DeserializeObject<JToken>(jsonText);
Parse(0, jtoken);
}
private static void Parse(int padding, JToken jtoken)
{
if (jtoken is JValue)
{
// プリミティブ型の場合 Value プロパティで値を取得
JValue jvalue = (JValue)jtoken;
string str = $"value = {jvalue.Value}";
Console.WriteLine(str.PadLeft(str.Length + padding));
}
else if (jtoken is JObject)
{
// JObject は IDictionary<string, JToken> を継承しているので、
// 以下のように foreach ループで {"name":"value"} を取得できる
foreach (KeyValuePair<string, JToken> kvp in (JObject)jtoken)
{
if (kvp.Value is JValue)
{
JValue jvalue = (JValue)kvp.Value;
string str = $"name = {kvp.Key}, value = {jvalue.Value}";
Console.WriteLine(str.PadLeft(str.Length + padding));
}
else if (kvp.Value is JObject)
{
string str = $"name = {kvp.Key}";
Console.WriteLine(str.PadLeft(str.Length + padding));
Parse(padding + 2, kvp.Value);
}
else if (kvp.Value is JArray)
{
string str = $"name = {kvp.Key}";
Console.WriteLine(str.PadLeft(str.Length + padding));
JArray jarray = (JArray)kvp.Value;
int index = 1;
// JArray は IList<JToken> を継承しているので、以下の
// ように foreach で配列の各要素の JToken を取得できる
foreach (JToken token in jarray)
{
string idx = $"array index {index}";
Console.WriteLine(idx.PadLeft(idx.Length + padding + 1));
Parse(padding + 2, token);
index++;
}
}
}
}
else if (jtoken is JArray)
{
JArray jarray = (JArray)jtoken;
int index = 1;
foreach (JToken token in jarray)
{
string str = $"array index {index}";
Console.WriteLine(str.PadLeft(str.Length + padding + 1));
Parse(padding + 2, token);
index++;
}
}
else
{
// ここには来ないはずだが念のため
Console.WriteLine(jtoken.ToString());
}
}
}
}
ちなみに、上のコードで使った JSON 文字列は以下の通りです。他にもいろいろ試してみましたが、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"]
}