はじめに
- キー・値形式のJSONを解析する場合、
JsonSerializer.Deserialize<Dictionary<T, V>>()
を使って簡単に実現できますが、キー・値の順番は保証されません。
ここでは、このキー・値の順番を保証する方法を紹介します。 - 次の環境を使用して動作確認しています。
OS Windows 10(64ビット) IDE Microsoft Visual Studio Community 2019(16.8.5) + C#(8.0) - 完全なソースコードはこちらで公開しています。
- なお、System.Text.Jsonでサポートされているコレクション型はこちらのリファレンスをご覧ください。
前提データ・クラス
サンプルで使用するJSONデータ、JSONデータを格納するクラスです。
- キー・値形式になっているJSONを使用します。
本来であれば、JsonSerializer.Deserialize<Dictionary<string, MyEntity>>()
で解析できます。123456789101112{"key1": {"value1": 111,"value2": "test1","value3": [1, 2, 3]},"key2": {"value1": 222,"value2": "test2","value3": [2, 3, 4]}} - 次のクラスにJSONデータ(キー・値形式)の値を格納する想定です。12345678class MyEntity{public int Value1 { get; set; }public string Value2 { get; set; }public int[] Value3 { get; set; }public override string ToString()=> $"{{Value1={Value1}, Value2={Value2}, Value3=[{string.Join(", ", Value3)}]}}";}
OrderedDictionaryを使用する方法
- 順番を保持できるDictionaryであるOrderedDictionary(System.Collections.Specialized)を使用できます。
- このクラスはジェネリックに対応していないため、値の解析処理を自身で実装する必要があります。
- OrderedDictionaryの列挙子としてDictionaryEntry型が返却されるので、そこからキーと値を取得できます。
- 値はJsonElement型として返却されるので、そこから数値や文字列を取得できます。詳細はこちらをご覧ください。
- 次の例では、JsonElementオブジェクトから個別に値を取得してMyEntityクラスに格納します。1234567891011121314var result = JsonSerializer.Deserialize<OrderedDictionary>(TestJson);foreach (DictionaryEntry e in result){var key = e.Key;var valElement = (JsonElement)e.Value;var entity = new MyEntity(){Value1 = valElement.GetProperty("value1").GetInt32(),Value2 = valElement.GetProperty("value2").GetString(),Value3 = valElement.GetProperty("value3").EnumerateArray().Select(je => je.GetInt32()).ToArray()};Console.WriteLine("{0}={1}", key, entity);}
- 次の例では、JsonElementオブジェクトから取得したJSONを再度デシリアライズしてMyEntityクラスに値を格納します。12345678910var result = JsonSerializer.Deserialize<OrderedDictionary>(TestJson);var options = new JsonSerializerOptions() { PropertyNameCaseInsensitive = true };foreach (DictionaryEntry e in result){var key = e.Key;var valElement = (JsonElement)e.Value;var valJson = valElement.GetRawText();var entity = JsonSerializer.Deserialize<MyEntity>(valJson, options);Console.WriteLine("{0}={1}", key, entity);}
JsonDocumentを使用する方法
上記の例では、Dictionaryの延長の位置づけでOrderedDictionaryを使う方法を紹介しました。
ジェネリックに対応しておらず、結局JsonElementを使って手動で解析する必要があります。
これだったら、素直にJsonDocumentを使って解析した方がいいかとも思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | using var document = JsonDocument.Parse(TestJson); JsonElement root = document.RootElement; foreach (var jsonProperty in root.EnumerateObject()) { var key = jsonProperty.Name; var valElement = jsonProperty.Value; var entity = new MyEntity() { Value1 = valElement.GetProperty("value1").GetInt32(), Value2 = valElement.GetProperty("value2").GetString(), Value3 = valElement.GetProperty("value3").EnumerateArray() .Select(ve => ve.GetInt32()).ToArray() }; Console.WriteLine("{0}={1}", key, entity); } |
参考)JsonElementの使い方
- Jsonデータの解析結果は、(内部的に)JsonElementで再帰的に表現されています。
- JsonElementは、値またはオブジェクト(入れ子になったデータ)を表現しています。
- 値の例:「”value1″: 111」というJSONの値「111」
- オブジェクトの例:「”key1″: { “value1”: 123, “value2”: “test1”,…}」というJSONの値「{ “value1”: 123, “value2”: “test1”,…}」
- 何を表現しているかはValueKind(JsonValueKind列挙型)で判定できます。
- 数値、文字列、真偽等の値を表現している場合、その値をGetInt32(), GetString()等で整数型や文字列として取得できます。値が配列形式の場合、EnumerateArray()で列挙子を取得できるので、ループ処理で配列値の取得や変換を行えます。
- オブジェクトを表現している場合、オブジェクトに含まれるキー・値(「”value1″: 111」等)はプロパティ(JsonProperty型)として保持されています。JsonPropertyのName, Valueがキー・値に対応しています。
- プロパティの一覧を取得する場合はEnumerateObject()を使用します。プロパティの値を直接取得する場合は、GetProperty(string)を使用します。
- JsonElementが表現する値やオブジェクトのJSONはGetRawText()で取得できます。