#json11とは
C++11で書かれたjsonライブラリで
使い方が綺麗なため、好まれて使われていると思います
私は、以前はpicojsonを使ってましたが最近はjson11を使ってます
https://github.com/dropbox/json11/blob/master
ここから json11.cppとjson11.hppをダウンロードして使ってます
#使い方
https://github.com/dropbox/json11/blob/master/test.cpp
にサンプルが書いているので読めばわかりますが、補足します
##jsonパース
サンプルのように
const string simple_test =
R"({"k1":"v1", "k2":42, "k3":["a",123,true,false,null]})";
string err;
auto json = json11::Json::parse(simple_test, err);
とすれば、簡単に文字列から jsonにパース出来ます。
json.dump() とすると、jsonの文字列が取得出来ます
パースエラーがあれば errにエラーメッセージが返ってきます
jsonは シングルクオートではダメなので、ダブルクオートのエスケープが面倒ですが
そんな時 R"()" をつかって ヒアドキュメントにすると 上記のように楽です
見てわかるとおり "k3":["a",123,true,false,null] これで配列要素を扱うことが出来ます
##値の取得
サンプルのように
cout << "k1=" << json["k1"].string_value() << endl;
cout << "k2=" << json["k2"].int_value() << endl;
for (auto &k : json["k3"].array_items()) {
cout << " - " << k.dump() << endl;
}
これで値を取得できますね。
string_value で文字列の値を
int_value で整数の値を
array_items で配列を取得します
他にも bool_value、number_value があります
ただし 64bit intの値は取得できないので
大きい整数は、number_valueで取得します。ただし 仮数部の52ビットまでしか扱えません
それ以上の値をとるには、stringで送るのが良いかもしれません
##json構築
サンプルのように
Json obj = Json::object({
{ "k1", "v1" },
{ "k2", 42.0 },
{ "k3", Json::array({ "a", 123.0, true, false, nullptr }) },
});
と、イニシャライズリストを使い、jsonを構築できます
とても綺麗な実装です。この綺麗さが json11の人気のポイントだと思います
配列はJson::array() で構築します
文字列で与えるのに比べ、コンパイル時に解決出来るので、おそらくこの方が早くなります
また、STLコンテナを使い、構築する事が可能です!
std::list<int> l1 { 1, 2, 3 };
std::vector<int> l2 { 1, 2, 3 };
std::set<int> l3 { 1, 2, 3 };
std::array<int,3> l4 { 1, 2, 3 };
cout << json11::Json(l1).dump() << endl;
cout << json11::Json(l2).dump() << endl;
cout << json11::Json(l3).dump() << endl;
cout << json11::Json(l4).dump() << endl;
これらはいずれも、dump()すると
[1,2,3]
という 配列になります
std::map<string, string> m1 { { "k1", "v1" }, { "k2", "v2" } };
std::multimap<string, string> m2 { { "k1", "v1" }, { "k2", "v2" } };
std::unordered_map<string, string> m3 { { "k1", "v1" }, { "k2", "v2" } };
std::unordered_multimap<string, string> m4 { { "k1", "v1" }, { "k2", "v2" } };
cout << json11::Json(m1).dump() << endl;
cout << json11::Json(m2).dump() << endl;
cout << json11::Json(m3).dump() << endl;
cout << json11::Json(m4).dump() << endl;
stl::map系を使うと、名前付きフィールドを構築できます
上記を実行するといずれも
{"k1": "v1", "k2": "v2"}
というjsonになります
とうぜん これらは複合させることが可能です
std::map<string, string> m1 { { "k1", "v1" }, { "k2", "v2" } };
std::list<int> l1 { 1, 2, 3 };
json11::Json json1 = json11::Json::object {
{ "key1", m1 },
{ "key2", l1 }
};
cout << json1.dump() << endl;
とすると
{"key1": {"k1": "v1", "k2": "v2"}, "key2": [1, 2, 3]}
が得られます。
もちろんjson同士を複合させられます
std::map<string, string> m1 { { "k1", "v1" }, { "k2", "v2" } };
std::list<int> l1 { 1, 2, 3 };
json11::Json json1 = json11::Json::object {
{ "key1", m1 },
{ "key2", l1 }
};
json11::Json json2 = json11::Json::object {
{ "json1", json1 },
{ "json2", "value" }
};
cout << json2.dump() << endl;
結果は
{"json1": {"key1": {"k1": "v1", "k2": "v2"}, "key2": [1, 2, 3]}, "json2": "value"}
json同士も連結できます
##同じ階層で連結させたい・・
上記だと
{"key1": {"k1": "v1", "k2": "v2"}, "key2": [1, 2, 3]}
{"json1": {"key1": {"k1": "v1", "k2": "v2"}, "key2": [1, 2, 3]}, "json2": "value"}
連結した際に、1階層はいってしまいます。フラットに連結できないか?
{"k1": "v1", "k2": "v2", "key2": [1, 2, 3]}
{"k1": "v1", "k2": "v2", "key2": [1, 2, 3], "json2": "value"}
このように。
ということで json11のコードを見ます
typedef std::vector<Json> array;
typedef std::map<std::string, Json> object;
// Constructors for the various types of JSON value.
Json() noexcept; // NUL
Json(std::nullptr_t) noexcept; // NUL
Json(double value); // NUMBER
Json(int value); // NUMBER
Json(bool value); // BOOL
Json(const std::string &value); // STRING
Json(std::string &&value); // STRING
Json(const char * value); // STRING
Json(const array &values); // ARRAY
Json(array &&values); // ARRAY
Json(const object &values); // OBJECT
Json(object &&values); // OBJECT
// Implicit constructor: anything with a to_json() function.
template <class T, class = decltype(&T::to_json)>
Json(const T & t) : Json(t.to_json()) {}
// Implicit constructor: map-like objects (std::map, std::unordered_map, etc)
template <class M, typename std::enable_if<
std::is_constructible<std::string, typename M::key_type>::value
&& std::is_constructible<Json, typename M::mapped_type>::value,
int>::type = 0>
Json(const M & m) : Json(object(m.begin(), m.end())) {}
// Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc)
template <class V, typename std::enable_if<
std::is_constructible<Json, typename V::value_type>::value,
int>::type = 0>
Json(const V & v) : Json(array(v.begin(), v.end())) {}
このように Json::objectは mapのtypedefであり
コンストラクタにて mapとして受け取っているため
イニシャライザリストにて、フラットに連結するのは 少し難しそうです
アクセッサも
Json::Type Json::type() const { return m_ptr->type(); }
double Json::number_value() const { return m_ptr->number_value(); }
int Json::int_value() const { return m_ptr->int_value(); }
bool Json::bool_value() const { return m_ptr->bool_value(); }
const string & Json::string_value() const { return m_ptr->string_value(); }
const vector<Json> & Json::array_items() const { return m_ptr->array_items(); }
const map<string, Json> & Json::object_items() const { return m_ptr->object_items(); }
const Json & Json::operator[] (size_t i) const { return (*m_ptr)[i]; }
const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key]; }
double JsonValue::number_value() const { return 0; }
int JsonValue::int_value() const { return 0; }
bool JsonValue::bool_value() const { return false; }
const string & JsonValue::string_value() const { return statics().empty_string; }
const vector<Json> & JsonValue::array_items() const { return statics().empty_vector; }
const map<string, Json> & JsonValue::object_items() const { return statics().empty_map; }
const Json & JsonValue::operator[] (size_t) const { return static_null(); }
const Json & JsonValue::operator[] (const string &) const { return static_null(); }
const Json & JsonObject::operator[] (const string &key) const {
auto iter = m_value.find(key);
return (iter == m_value.end()) ? static_null() : iter->second;
}
const Json & JsonArray::operator[] (size_t i) const {
if (i >= m_value.size()) return static_null();
else return m_value[i];
}
と、頑なに constなので、難しそうです
(非constメンバを作り 内部のm_ptrにアクセス出来るようにすれば、jsonノードを任意でいじれるようになるかもしれません)
とりあえずは、方針として
Json::object(map) を作成し insertていく
これでやってみようと思います
std::map<string, string> m1 { { "k1", "v1" }, { "k2", "v2" } };
std::list<int> l1 { 1, 2, 3 };
json11::Json::object jo(m1.begin(),m1.end());
jo.insert(pair<string,json11::Json>("key2", l1));
cout << json1.dump() << endl;
{"k1": "v1", "k2": "v2", "key2": [1, 2, 3]}
が出力されました。
このように Json::objectを直接操作すれば、任意のjsonを作る事が出来そうです。
#まとめ
json11は綺麗だが、jsonの生成に関しては今のところ使いやすい印象がない。
特に 任意のノードにノードを連結しようと思うと不便を感じる
結局は mapを直接触る事になるんだろうか?
詳しい方、ヘルプ!