前書き
友達に頼まれ、Unityでの作業を手伝う機会があったのですが、C#ではJson読み込みでひと工夫必要らしく、結構苦労して調べたので共有という感じの記事になります。
結論
Newtonsoftというライブラリを使用します。
というのも、JsonUtilityなどでも読み込めることは読み込めますが、事前にjsonの構造に対応付けたクラスを作成する必要があったり、また例えば辞書の配列など複雑なデータ型は読み込めないようです。(NULLになる)
他の言語のように、json文字列から一発で辞書型に対応付けてほしいなぁと思っていたところ、Newtonsoftというライブラリを使用すると一発でできるそうだったので、以下このライブラリの説明になります。
使用する名前空間
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
ソースコード上部に上記を追加します。
※ Unity2020からはデフォルトでインストールされているそうです
文字列からJObjectへの変換
string jsonText = "{ ... }"; // 何かしらのJSON文字列
JObject obj = JObject.Parse(jsonText); // これでJSONがまとめてパースされる
JObjectの読み込み
これでobj
変数にJSONのパース結果のJObject
というオブジェクトが格納されました。
またJObject
の中にはJToken
というオブジェクトなどが入れ子になって入っているイメージになるそうです。
参考ページ
真下のオブジェクトを取得
obj["key_name"]; // JTokenオブジェクトが帰ってくる
真下の値を取得
obj["key_name"].Value<int>(); // intの所には要素の型名を入れる
入れ子のオブジェクトを取得
obj["key1"]["key2"]; // JTokenオブジェクトが帰ってくる
配列を取得
foreach文で回し、用意していたListにAddしていく感じになります。
string jsonText = "{ 'key1': [1, 2, 3] }";
var obj = JObject.Parse(jsonText);
var arr = new List<int>(); // 取得結果がこのリストに格納される
foreach (var jtoken in obj["key1"].Children())
{
arr.Add(token.Value<int>());
}
Debug.Log(arr);
ネストした辞書を取得
SelectToken()
というメソッドを使うと、ネストした辞書の要素にアクセス可能になります。
取得結果はJToken
などのオブジェクトになるので、Values<型名>()
で変換します。
string jsonText = @"
{
'key1': {
'key2': {
'key3': 10
}
}
}
";
var obj = JObject.Parse(jsonText);
var num = obj.SelectToken("key1.key2.key3").Value<int>();
// 10が出力される
Debug.Log(num);
辞書の配列を取得
調べたところSelectToken("key1[0].value1")
などの書き方が可能とのこと。
SelectToken
の第一引数にはJSONPathというものを指定できるため。
SelectToken
にJSONPathを指定してValue<型名>()を使い、要素を取得。
string jsonText = @"
{
'key1': [
{
'key2': 'value2',
'key3': 'value3'
},
{
'key4': 'value4',
'key5': 'value5'
}
]
}
";
var obj = JObject.Parse(jsonText);
// key1[1]のようにSelectTokenでは添え字も指定可能
// JSONPathというXMLPathのような規格に準拠しているとのこと
var value5 = obj.SelectToken("key1[1].key5").Value<string>();
// "value5"が出力される
Debug.Log(value5);
(参考) ソースコード例
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public class NewBehaviourScript : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
// JSON文字列をファイルから読み込み
string json = Resources.Load<TextAsset>("test").ToString();
// JSON文字列をJTokenオブジェクトにパース
JObject obj = JObject.Parse(json);
// 辞書の配列を回す
foreach (var dic in obj["part_candidates"])
{
Debug.Log(dic);
// 辞書の中身を読み込む
// {"1": [0.0, 0.1, 0.2], "2": [0.0, 0.1, 0.2]} のようなデータ
foreach (var key in dic.Children())
{
// double配列の中身を保持するリスト
List<double> arr = new List<double>();
foreach (var value in key.Children())
{
// 配列の中の小数を配列に追加する
foreach (var n in value.Children())
{
arr.Add(n.Value<double>());
}
}
double x = arr[0];
double y = arr[1];
double z = arr[2];
Debug.Log(x + ", " + y + ", " + z);
}
}
}
// Update is called once per frame
void Update()
{
}
}
以上