json
オブジェクトを作成する世の中には無数の JSON ライブラリがあり、それぞれに存在理由があるかもしれません。私たちのクラスには次のような設計目標がありました。
直感的な構文。 Python などの言語では、JSON はファーストクラスのデータ型のように思えます。最新の C++ の演算子の魔法をすべて使用して、コード内で同じ感覚を実現しました。以下の例をチェックしてください。そうすれば、私の言いたいことがわかるでしょう。
簡単な統合。コード全体は、単一のヘッダー ファイルjson.hpp
で構成されています。それでおしまい。ライブラリ、サブプロジェクト、依存関係、複雑なビルド システムはありません。このクラスはバニラ C++11 で記述されています。全体として、コンパイラ フラグやプロジェクト設定を調整する必要はありません。
本格的なテスト。私たちのコードは十分に単体テストされており、すべての例外的な動作を含むコードの 100% をカバーしています。さらに、Valgrind と Clang Sanitizers でメモリ リークがないことを確認しました。 Google OSS-Fuzz はさらに、すべてのパーサーに対してファズ テストを 24 時間年中無休で実行し、これまでに数十億のテストを効果的に実行しています。高品質を維持するために、プロジェクトはコア インフラストラクチャ イニシアチブ (CII) のベスト プラクティスに従っています。
他の側面は私たちにとってそれほど重要ではありませんでした。
メモリ効率。各 JSON オブジェクトには、1 つのポインター (共用体の最大サイズ) と 1 つの列挙要素 (1 バイト) のオーバーヘッドがあります。デフォルトの汎化では、次の C++ データ型が使用されます。文字列の場合はstd::string
、数値の場合はint64_t
、 uint64_t
またはdouble
、オブジェクトの場合はstd::map
、配列の場合はstd::vector
、ブール値の場合はbool
。ただし、必要に応じて汎用クラスのbasic_json
テンプレート化できます。
スピード。確かに、より高速な JSON ライブラリは存在します。ただし、単一ヘッダーで JSON サポートを追加することで開発を高速化することが目標の場合は、このライブラリが適しています。 std::vector
またはstd::map
使用方法を知っている場合は、すでに設定は完了しています。
詳細については、投稿ガイドラインを参照してください。
GitHub スポンサーでこのライブラリのスポンサーになることができます。
みんなありがとう!
❓ ご質問がある場合は、 FAQまたはQ&Aセクションですでに回答されているかどうかを確認してください。そうでない場合は、そこで新しい質問をしてください。
ライブラリの使用方法について詳しく知りたい場合は、 READMEの残りの部分を確認するか、コード例を参照するか、ヘルプ ページを参照してください。
? APIについてさらに詳しく知りたい場合は、 「API リファレンス」を参照してください。
?バグを見つけた場合は、それが既知の問題なのか、それとも設計上の決定の結果なのか、 FAQ を確認してください。新しい問題を作成する前に、問題リストもご覧ください。問題を理解して再現できるよう、できるだけ多くの情報を提供してください。
完全なドキュメントをオフライン リソースとして含むドキュメント ブラウザー Dash、Velocity、および Zeal 用のdocsetもあります。
クラスの使用方法を理解するために、いくつかの例を示します。
以下の例のほかに、次のようなこともできます。
→ ドキュメントを確認してください
→ スタンドアロンのサンプル ファイルを参照する
すべての API 関数 (API ドキュメントに文書化されている) には、対応するスタンドアロンのサンプル ファイルがあります。たとえば、 emplace()
関数には、対応する emplace.cpp サンプル ファイルがあります。
json
クラスは、JSON 値を操作するための API を提供します。 JSON ファイルを読み取ってjson
オブジェクトを作成するには:
# include < fstream >
# include < nlohmann/json.hpp >
using json = nlohmann::json;
// ...
std::ifstream f ( " example.json " );
json data = json::parse(f);
json
オブジェクトを作成するこのリテラル JSON 値をファイル内にjson
オブジェクトとしてハードコーディングするとします。
{
"pi" : 3.141 ,
"happy" : true
}
さまざまなオプションがあります。
// Using (raw) string literals and json::parse
json ex1 = json::parse( R"(
{
"pi": 3.141,
"happy": true
}
)" );
// Using user-defined (raw) string literals
using namespace nlohmann ::literals ;
json ex2 = R"(
{
"pi": 3.141,
"happy": true
}
)" _json;
// Using initializer lists
json ex3 = {
{ " happy " , true },
{ " pi " , 3.141 },
};
クラスの使用方法を理解するために、いくつかの例を示します。
JSON オブジェクトを作成するとします。
{
"pi" : 3.141 ,
"happy" : true ,
"name" : " Niels " ,
"nothing" : null ,
"answer" : {
"everything" : 42
},
"list" : [ 1 , 0 , 2 ],
"object" : {
"currency" : " USD " ,
"value" : 42.99
}
}
このライブラリを使用すると、次のように記述できます。
// create an empty structure (null)
json j;
// add a number that is stored as double (note the implicit conversion of j to an object)
j[ " pi " ] = 3.141 ;
// add a Boolean that is stored as bool
j[ " happy " ] = true ;
// add a string that is stored as std::string
j[ " name " ] = " Niels " ;
// add another null object by passing nullptr
j[ " nothing " ] = nullptr ;
// add an object inside the object
j[ " answer " ][ " everything " ] = 42 ;
// add an array that is stored as std::vector (using an initializer list)
j[ " list " ] = { 1 , 0 , 2 };
// add another object (using an initializer list of pairs)
j[ " object " ] = { { " currency " , " USD " }, { " value " , 42.99 } };
// instead, you could also write (which looks very similar to the JSON above)
json j2 = {
{ " pi " , 3.141 },
{ " happy " , true },
{ " name " , " Niels " },
{ " nothing " , nullptr },
{ " answer " , {
{ " everything " , 42 }
}},
{ " list " , { 1 , 0 , 2 }},
{ " object " , {
{ " currency " , " USD " },
{ " value " , 42.99 }
}}
};
これらすべての場合において、使用する JSON 値の型をコンパイラーに「伝える」必要はまったくないことに注意してください。明示的にしたい場合、またはいくつかの特殊なケースを表現したい場合は、関数json::array()
およびjson::object()
が役に立ちます。
// a way to express the empty array []
json empty_array_explicit = json::array();
// ways to express the empty object {}
json empty_object_implicit = json({});
json empty_object_explicit = json::object();
// a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]]
json array_not_object = json::array({ { " currency " , " USD " }, { " value " , 42.99 } });
_json
文字列リテラルに追加することで、JSON 値を作成 (逆シリアル化) できます。
// create object from string literal
json j = " { " happy " : true, " pi " : 3.141 } " _json;
// or even nicer with a raw string literal
auto j2 = R"(
{
"happy": true,
"pi": 3.141
}
)" _json;
_json
サフィックスを追加しないと、渡された文字列リテラルは解析されず、単に JSON 文字列値として使用されることに注意してください。つまり、 json j = "{ "happy": true, "pi": 3.141 }"
実際の文字列を解析するのではなく、文字列"{ "happy": true, "pi": 3.141 }"
を保存するだけです。物体。
文字列リテラルはusing namespace nlohmann::literals;
スコープに含める必要があります。 ( json::parse()
を参照)。
上記の例は、 json::parse()
使用して明示的に表現することもできます。
// parse explicitly
auto j3 = json::parse( R"( {"happy": true, "pi": 3.141} )" );
JSON 値の文字列表現を取得することもできます (シリアル化)。
// explicit conversion to string
std::string s = j.dump(); // {"happy":true,"pi":3.141}
// serialization with pretty printing
// pass in the amount of spaces to indent
std::cout << j.dump( 4 ) << std::endl;
// {
// "happy": true,
// "pi": 3.141
// }
シリアル化と代入の違いに注意してください。
// store a string in a JSON value
json j_string = " this is a string " ;
// retrieve the string value
auto cpp_string = j_string. template get<std::string>();
// retrieve the string value (alternative when a variable already exists)
std::string cpp_string2;
j_string.get_to(cpp_string2);
// retrieve the serialized value (explicit JSON serialization)
std::string serialized_string = j_string.dump();
// output of original string
std::cout << cpp_string << " == " << cpp_string2 << " == " << j_string. template get<std::string>() << ' n ' ;
// output of serialized value
std::cout << j_string << " == " << serialized_string << std::endl;
.dump()
最初に保存された文字列値を返します。
このライブラリは UTF-8 のみをサポートしていることに注意してください。ライブラリに異なるエンコーディングの文字列を格納する場合、 json::error_handler_t::replace
またはjson::error_handler_t::ignore
エラー ハンドラーとして使用されていない限り、 dump()
を呼び出すと例外がスローされることがあります。
ストリームを使用してシリアル化および逆シリアル化することもできます。
// deserialize from standard input
json j;
std::cin >> j;
// serialize to standard output
std::cout << j;
// the setw manipulator was overloaded to set the indentation for pretty printing
std::cout << std::setw( 4 ) << j << std::endl;
これらの演算子はstd::istream
またはstd::ostream
のサブクラスに対して機能します。ファイルを使用した同じ例を次に示します。
// read a JSON file
std::ifstream i ( " file.json " );
json j;
i >> j;
// write prettified JSON to another file
std::ofstream o ( " pretty.json " );
o << std::setw( 4 ) << j << std::endl;
failbit
の例外ビットを設定することは、この使用例には不適切であることに注意してください。 noexcept
指定子が使用されているため、プログラムが終了します。
イテレータ範囲から JSON を解析することもできます。つまり、 value_type
が 1、2、または 4 バイトの整数型である反復子によってアクセス可能なコンテナからです。これらはそれぞれ UTF-8、UTF-16、UTF-32 として解釈されます。たとえば、 std::vector<std::uint8_t>
、またはstd::list<std::uint16_t>
:
std::vector<std:: uint8_t > v = { ' t ' , ' r ' , ' u ' , ' e ' };
json j = json::parse(v.begin(), v.end());
範囲 [begin, end) の反復子をそのままにすることもできます。
std::vector<std:: uint8_t > v = { ' t ' , ' r ' , ' u ' , ' e ' };
json j = json::parse(v);
解析関数は任意のイテレータ範囲を受け入れるため、 LegacyInputIterator
概念を実装することで独自のデータ ソースを提供できます。
struct MyContainer {
void advance ();
const char & get_current ();
};
struct MyIterator {
using difference_type = std:: ptrdiff_t ;
using value_type = char ;
using pointer = const char *;
using reference = const char &;
using iterator_category = std::input_iterator_tag;
MyIterator& operator ++() {
target-> advance ();
return * this ;
}
bool operator !=( const MyIterator& rhs) const {
return rhs. target != target;
}
reference operator *() const {
return target-> get_current ();
}
MyContainer* target = nullptr ;
};
MyIterator begin (MyContainer& tgt) {
return MyIterator{&tgt};
}
MyIterator end ( const MyContainer&) {
return {};
}
void foo () {
MyContainer c;
json j = json::parse (c);
}
このライブラリは、次の関数を備えた SAX のようなインターフェイスを使用します。
// called when null is parsed
bool null ();
// called when a boolean is parsed; value is passed
bool boolean ( bool val);
// called when a signed or unsigned integer number is parsed; value is passed
bool number_integer ( number_integer_t val);
bool number_unsigned ( number_unsigned_t val);
// called when a floating-point number is parsed; value and original string is passed
bool number_float ( number_float_t val, const string_t & s);
// called when a string is parsed; value is passed and can be safely moved away
bool string ( string_t & val);
// called when a binary value is parsed; value is passed and can be safely moved away
bool binary ( binary_t & val);
// called when an object or array begins or ends, resp. The number of elements is passed (or -1 if not known)
bool start_object (std:: size_t elements);
bool end_object ();
bool start_array (std:: size_t elements);
bool end_array ();
// called when an object key is parsed; value is passed and can be safely moved away
bool key ( string_t & val);
// called when a parse error occurs; byte position, the last token, and an exception is passed
bool parse_error (std:: size_t position, const std::string& last_token, const detail:: exception & ex);
各関数の戻り値によって、解析を続行するかどうかが決まります。
独自の SAX ハンドラーを実装するには、次の手順を実行します。
nlohmann::json_sax<json>
基本クラスとして使用できますが、上記の関数が実装されパブリックである任意のクラスを使用することもできます。my_sax
を作成します。bool json::sax_parse(input, &my_sax)
を呼び出します。ここで、最初のパラメータは文字列や入力ストリームなどの任意の入力であり、2 番目のパラメータは SAX インターフェイスへのポインタです。 sax_parse
関数は、最後に実行された SAX イベントの結果を示すbool
のみを返すことに注意してください。 json
値は返されません。SAX イベントをどう処理するかはユーザーが決定します。さらに、 parse_error
エラーの場合には例外はスローされません。parse_error 実装に渡された例外オブジェクトをどう処理するかはユーザー次第です。内部的には、SAX インターフェースは DOM パーサー (クラスjson_sax_dom_parser
) およびアクセプター ( json_sax_acceptor
) に使用されます。ファイルjson_sax.hpp
を参照してください。
JSON クラスは STL コンテナーと同じように動作するように設計されました。実際、これはReversibleContainer の要件を満たしています。
// create an array using push_back
json j;
j.push_back( " foo " );
j.push_back( 1 );
j.push_back( true );
// also use emplace_back
j.emplace_back( 1.78 );
// iterate the array
for (json::iterator it = j.begin(); it != j.end(); ++it) {
std::cout << *it << ' n ' ;
}
// range-based for
for ( auto & element : j) {
std::cout << element << ' n ' ;
}
// getter/setter
const auto tmp = j[ 0 ]. template get<std::string>();
j[ 1 ] = 42 ;
bool foo = j.at( 2 );
// comparison
j == R"( ["foo", 1, true, 1.78] )" _json; // true
// other stuff
j.size(); // 4 entries
j.empty(); // false
j.type(); // json::value_t::array
j.clear(); // the array is empty again
// convenience type checkers
j.is_null();
j.is_boolean();
j.is_number();
j.is_object();
j.is_array();
j.is_string();
// create an object
json o;
o[ " foo " ] = 23 ;
o[ " bar " ] = false ;
o[ " baz " ] = 3.141 ;
// also use emplace
o.emplace( " weather " , " sunny " );
// special iterator member functions for objects
for (json::iterator it = o.begin(); it != o.end(); ++it) {
std::cout << it. key () << " : " << it. value () << " n " ;
}
// the same code as range for
for ( auto & el : o.items()) {
std::cout << el. key () << " : " << el. value () << " n " ;
}
// even easier with structured bindings (C++17)
for ( auto & [key, value] : o.items()) {
std::cout << key << " : " << value << " n " ;
}
// find an entry
if (o.contains( " foo " )) {
// there is an entry with key "foo"
}
// or via find and an iterator
if (o.find( " foo " ) != o.end()) {
// there is an entry with key "foo"
}
// or simpler using count()
int foo_present = o.count( " foo " ); // 1
int fob_present = o.count( " fob " ); // 0
// delete an entry
o.erase( " foo " );
JSON 値 (整数、浮動小数点数、ブール値、文字列など) を構築するために値を使用できる任意のシーケンス コンテナー ( std::array
、 std::vector
、 std::deque
、 std::forward_list
、 std::list
)タイプ、またはこのセクションで説明する STL コンテナ) を使用して、JSON 配列を作成できます。同様の連想コンテナー ( std::set
、 std::multiset
、 std::unordered_set
、 std::unordered_multiset
) にも同じことが当てはまりますが、これらの場合、配列の要素の順序は、要素が配列内でどのように順序付けされているかによって決まります。それぞれの STL コンテナ。
std::vector< int > c_vector { 1 , 2 , 3 , 4 };
json j_vec (c_vector);
// [1, 2, 3, 4]
std::deque< double > c_deque { 1.2 , 2.3 , 3.4 , 5.6 };
json j_deque (c_deque);
// [1.2, 2.3, 3.4, 5.6]
std::list< bool > c_list { true , true , false , true };
json j_list (c_list);
// [true, true, false, true]
std::forward_list< int64_t > c_flist { 12345678909876 , 23456789098765 , 34567890987654 , 45678909876543 };
json j_flist (c_flist);
// [12345678909876, 23456789098765, 34567890987654, 45678909876543]
std::array< unsigned long , 4 > c_array {{ 1 , 2 , 3 , 4 }};
json j_array (c_array);
// [1, 2, 3, 4]
std::set<std::string> c_set { " one " , " two " , " three " , " four " , " one " };
json j_set (c_set); // only one entry for "one" is used
// ["four", "one", "three", "two"]
std::unordered_set<std::string> c_uset { " one " , " two " , " three " , " four " , " one " };
json j_uset (c_uset); // only one entry for "one" is used
// maybe ["two", "three", "four", "one"]
std::multiset<std::string> c_mset { " one " , " two " , " one " , " four " };
json j_mset (c_mset); // both entries for "one" are used
// maybe ["one", "two", "one", "four"]
std::unordered_multiset<std::string> c_umset { " one " , " two " , " one " , " four " };
json j_umset (c_umset); // both entries for "one" are used
// maybe ["one", "two", "one", "four"]
同様に、キーがstd::string
を構築でき、その値が JSON 値の構築に使用できる、任意の連想キーと値のコンテナー ( std::map
、 std::multimap
、 std::unordered_map
、 std::unordered_multimap
)上記の例) を使用して、JSON オブジェクトを作成できます。マルチマップの場合、JSON オブジェクトで使用されるキーは 1 つだけであり、値は STL コンテナの内部順序に依存することに注意してください。
std::map<std::string, int > c_map { { " one " , 1 }, { " two " , 2 }, { " three " , 3 } };
json j_map (c_map);
// {"one": 1, "three": 3, "two": 2 }
std::unordered_map< const char *, double > c_umap { { " one " , 1.2 }, { " two " , 2.3 }, { " three " , 3.4 } };
json j_umap (c_umap);
// {"one": 1.2, "two": 2.3, "three": 3.4}
std::multimap<std::string, bool > c_mmap { { " one " , true }, { " two " , true }, { " three " , false }, { " three " , true } };
json j_mmap (c_mmap); // only one entry for key "three" is used
// maybe {"one": true, "two": true, "three": true}
std::unordered_multimap<std::string, bool > c_ummap { { " one " , true }, { " two " , true }, { " three " , false }, { " three " , true } };
json j_ummap (c_ummap); // only one entry for key "three" is used
// maybe {"one": true, "two": true, "three": true}
このライブラリは、構造化された値に対処する代替手段としてJSON ポインター(RFC 6901) をサポートしています。これに加えて、 JSON Patch (RFC 6902) を使用すると、2 つの JSON 値の違いを記述することができ、Unix で知られている patch および diff 操作が効果的に可能になります。
// a JSON value
json j_original = R"( {
"baz": ["one", "two", "three"],
"foo": "bar"
} )" _json;
// access members with a JSON pointer (RFC 6901)
j_original[ " /baz/1 " _json_pointer];
// "two"
// a JSON patch (RFC 6902)
json j_patch = R"( [
{ "op": "replace", "path": "/baz", "value": "boo" },
{ "op": "add", "path": "/hello", "value": ["world"] },
{ "op": "remove", "path": "/foo"}
] )" _json;
// apply the patch
json j_result = j_original.patch(j_patch);
// {
// "baz": "boo",
// "hello": ["world"]
// }
// calculate a JSON patch from two JSON values
json::diff (j_result, j_original);
// [
// { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] },
// { "op": "remove","path": "/hello" },
// { "op": "add", "path": "/foo", "value": "bar" }
// ]
このライブラリは、パッチ形式としてJSON Merge Patch (RFC 7386) をサポートしています。 JSON ポインター (上記を参照) を使用して操作する値を指定するのではなく、変更されるドキュメントを厳密に模倣した構文を使用して変更を記述します。
// a JSON value
json j_document = R"( {
"a": "b",
"c": {
"d": "e",
"f": "g"
}
} )" _json;
// a patch
json j_patch = R"( {
"a":"z",
"c": {
"f": null
}
} )" _json;
// apply the patch
j_document.merge_patch(j_patch);
// {
// "a": "z",
// "c": {
// "d": "e"
// }
// }
サポートされている型は暗黙的に JSON 値に変換できます。
JSON 値からの暗黙的な変換は使用しないことをお勧めします。この推奨事項の詳細については、こちらをご覧ください。 json.hpp
ヘッダーを含める前にJSON_USE_IMPLICIT_CONVERSIONS
0
に定義することで、暗黙的な変換をオフに切り替えることができます。 CMake を使用する場合、オプションJSON_ImplicitConversions
OFF
に設定することによってもこれを実現できます。
// strings
std::string s1 = " Hello, world! " ;
json js = s1;
auto s2 = js. template get<std::string>();
// NOT RECOMMENDED
std::string s3 = js;
std::string s4;
s4 = js;
// Booleans
bool b1 = true ;
json jb = b1;
auto b2 = jb. template get< bool >();
// NOT RECOMMENDED
bool b3 = jb;
bool b4;
b4 = jb;
// numbers
int i = 42