はじめに
JSONパーサのparsonが便利だったのでまとめておきます。今回はJSONの読み込みではなく成型に絞った内容です。JSONって何?という方はこちら。
最も単純な例
細かい話は後回しにして、単純な例を見てみましょう。
JSON_Value* test_val = json_value_init_object(); // オブジェクトと紐づくよう初期化
JSON_Object* test_obj = json_value_get_object(test_val); // 紐づける
// メンバをセット
json_object_set_string(test_obj, "key_str", "example");
json_object_set_number(test_obj, "key_num", 1.0);
json_object_set_boolean(test_obj, "key_bool", true);
// 何かしらの処理
// 最後に解放処理
json_value_free(test_val);
結果は以下の通りです。
{
"key_str": "example",
"key_num": 1,
"key_bool": true
}
一番ややこしいのは、JSON_ValueとJSON_Objectの2つが存在するところです。
JSON_Valueについては後で説明するので一旦保留にしておきます。
一方、JSON_Objectとは、中カッコで囲まれ、中にいくつかメンバを持つもので、単にオブジェクトと呼んでいるものです(まさに上に表示されているもの)。
JSON_Valueとは?
こちらでも紹介されているように、バリューとして取り得る型には6つの種類があります。JSON_Valueとは、その6種類のどれとでも紐づくことができる、より上位の存在です。
上の例では、
- バリューを「あなたはオブジェクトと紐づくんですよ」と初期化
- 実際にオブジェクトと紐づける
- オブジェクトの方にメンバをセットしていく
という流れになっています。
上の例だけを見ていると、「JSON_Valueなんていらないのでは?」と思ってしまうかもしれませんが、これが後々になって生きてきます。
もっと複雑なJSONを作りたい!
上記の例では、バリューの型としてそれぞれ文字列・数値・booleanの3つをセットできました。基本はこれで押さえているのですが、先ほど作ったJSONを、下のようにさらに入れ子にするにはどうすればいいでしょうか?
{
"key_obj": {
"key_str": "example",
"key_num": 1,
"key_bool": true
}
}
てっきりjson_object_set_object()のような関数があるかと思いきや、実は、文字列や数値の場合とは異なり、オブジェクトを直接セットする関数はないんです。私はここで詰まりました。
間接的にオブジェクトをセットする
JSON_Objectは直接セットできません。その代わりに、JSON_Valueをセットする関数json_object_set_value()が用意されています。この関数を使って入れ子にしたコードがこちら。
// 親となるオブジェクトを作成
JSON_Value* root_val = json_value_init_object();
JSON_Object* root_obj = json_value_get_object(root_val);
// 入れ子になるオブジェクトを作成
JSON_Value* test_val = json_value_init_object();
JSON_Object* test_obj = json_value_get_object(test_val);
json_object_set_string(test_obj, "key_str", "example");
json_object_set_number(test_obj, "key_num", 1.0);
json_object_set_boolean(test_obj, "key_bool", true);
// 親オブジェクトにメンバをセット
json_object_set_value(root_obj, "key_obj", test_val);
// 何かしらの処理
// 最後に親のみ解放
json_value_free(root_val);
繰り返しになりますが、結果は以下の通りです。
{
"key_obj": {
"key_str": "example",
"key_num": 1,
"key_bool": true
}
}
さらに外側に親となるJSON_Objectを作り、json_object_set_value()を呼ぶのがミソです。
ちなみに、最後の解放処理は、どれだけ入れ子になっても最上位さえ解放すればOKです。
間接的に配列をセットする
オブジェクトと同じようにして配列もセットすることができます。
オブジェクトの場合と流れは同じですが、初期化や要素の追加方法が若干異なります。
// 親となるオブジェクトを作成
JSON_Value* root_val = json_value_init_object();
JSON_Object* root_obj = json_value_get_object(root_val);
// 配列を作成
JSON_Value* test_val = json_value_init_array();
JSON_Array* test_arr = json_value_get_array(test_val);
json_array_append_string(test_arr, "example1");
json_array_append_string(test_arr, "example2");
// 親オブジェクトにメンバをセット
json_object_set_value(root_obj, "key_arr", test_val);
// 何かしらの処理
// 最後に親のみ解放
json_value_free(root_val);
結果は以下の通りです。
{
"key_arr": [
"example1",
"example2"
]
}
まとめ
以上のコードを流用すれば、入れ子式に複雑なJSONも成型することができるはずです。
もっと効率のいい方法は、こちらのSerializationを参考にどうぞ。dotset系の関数を使うともっとシンプルにできます。