JSON リンク ライブラリは、パフォーマンスが高く、割り当てが不要な、以下をサポートする C++ JSON ライブラリです。
このライブラリは、混合可能な他の解析モードもサポートしています。
json_array_iterator
またはjson_array_range
タイプを使用できます。json_value
タイプその他の注目すべき機能は次のとおりです。
boost::multiprecision::cpp_int
や GNU BigNum/Rational mpq_t
などの他の数値型との間で解析可能ライブラリはBSLライセンスを使用しています
JSON ドキュメントの構造がわかっている場合、解析は次のようになります。
MyThing thing = daw::json::from_json<MyThing>( json_string );
または、ドキュメントのルートが配列である配列ドキュメントの場合、それを簡単にするヘルパー メソッドがあり、次のように解析できます。
std::vector<MyThing> things = daw::json::from_json_array<MyThing>( json_string2 );
JSON ドキュメントの構造が不明な場合は、ビューとして機能し、オンデマンドでの反復とプル解析を可能にするjson_value
構築できます。以下は、JSON データからjson_value
を開く例です。
json_value val = daw::json::json_value( json_string );
from_json
メソッドとto_json
メソッドを使用すると、ほとんどの解析とシリアル化のニーズにアクセスできます。
イベントベースのパーサー (SAX) はdaw::json::json_event_parser
経由で呼び出すことができます。これは、json ドキュメントとイベント ハンドラーの 2 つの引数を取ります。イベント ハンドラーは、次のいずれかのメンバーを持つことでイベントをオプトインできます。
クラスの JSON ドキュメントへのマッピングは、特性daw::json::json_data_contract
を特殊化することによって行われます。マップされたクラスが別のマップされたクラスのメンバーである場合、再度マップする必要はありません。特性json_data_contract
には 2 つの部分があります。1 つは、JSON メンバーをクラスのコンストラクターにマップするtype
という名前の型エイリアスです。これにより、シリアル化するデータもクラスの構築に必要であると仮定して、クラスへのプライベート アクセスが必要になります。例えば:
struct Thing {
int a;
int b;
};
Thing
の構成には 2 つの整数が必要で、次の JSON があるとします。
{
"a" : 42 ,
"b" : 1234
}
次のようなマッピングを行うことができます。
namespace daw ::json {
template <>
struct json_data_contract <Thing> {
static constexpr char const a[] = " a " ;
static constexpr char const b[] = " b " ;
using type = json_member_list<
json_number<a, int >,
json_number<b, int >
>;
};
}
これは、ドキュメント内の JSON クラスには、整数である少なくとも 2 つのメンバー「a」と「b」が含まれることを示しています。これらは、 daw::json::from_json<Thing>( json_doc );
のときにThing
のコンストラクターに渡されます。が呼び出されているか、別のクラスにjson_class<MemberName, Thing>
メンバー マッピングがあることを示します。上記は名前の C++17 マッピング方法であり、将来の C++ バージョンでも機能します。ただし、C++20 以降では、名前をマッピング内にインラインで使用できます (例: json_number<"a", int>
。 JSON を解析するために必要なのは上記のすべてです。トレイトで静的メンバー関数をシリアル化するために必要です。前の例を拡張して、次のようにThing
シリアル化できます。
namespace daw ::json {
template <>
struct json_data_contract <Thing> {
static constexpr char const a[] = " a " ;
static constexpr char const b[] = " b " ;
using type = json_member_list<
json_number<a, int >,
json_number<b, int >
>;
};
static auto to_json_data ( Thing const & v ) {
return std::forward_as_tuple ( v. a , v. b );
}
}
タプルとして返されるメンバーの順序は、型別名type
のマッピングと一致する必要があります。これにより、データ メンバーがパブリックでない場合でも、アクセサー メソッドの結果を渡すことができます。また、クラスThing
int, int
から構築可能である必要があります。このライブラリは、C++17 の通常のコンストラクターと集約 init ( Thing{ int, int }
およびThing( int, int )
) の両方をサポートします。
to_json_data
の戻り値の型は、既存のオブジェクト メンバーへの参照のタプルを返す必要はありませんが、計算値を返すこともできます。右辺値は多くの場合一時的なものであるため、通過させることはできず、長距離のデバッグが必要になる可能性があります。ライブラリはこれに対して static_assert を行い、 <daw/daw_tuple_forward.h>
をインクルードし、一時データを保存して他の参照タイプを転送するdaw::forward_nonrvalue_as_tuple( ... )
を呼び出すことを提案します。パーサーは、クラスのコンストラクターへの呼び出しの適切な位置に各引数を構築することによって機能します。個々の引数パーサーは、データの指定された状況 (浮動小数点や整数など) に合わせて調整できます。次に、C++ クラスの構築に必要な引数とその順序を定義する型特性を使用して、JSON 内の各メンバーを確認できます。次に、各パーサーの結果を使用して値を構築します。 T{ parse<0, json_string<"name">>( data ), parse<1, json_number<"age", unsigned>>( data ), parse<json_number<2, "number>>( data )}
と同様メンバーごとに、解析する必要があるメンバーが見つかるまでデータ ストリームが進められ、後で解析するために関心のある場所が保存されます。このプロセスにより、他のクラスもメンバーとして解析できるようになります。 json_class<"member_name", Type>
マッピング タイプ。そのため、各マッピング特性は、その詳細ではなく特定のメンバーのみを処理する必要があります。
ルート値、配列要素、一部のキー値型、名前がno_name
となるバリアント要素リストなどの名前のないコンテキストでは、JSON マッピング型の代わりにいくつかのネイティブ C++ データ型を使用できます。これには、整数、浮動小数点、ブール値、std::string、std::string_view、連想コンテナ、シーケンス コンテナ、Nullable/Optional のような型、および以前にマップされたクラスが含まれます。
たとえば、文字列の配列をマップします。
template <>
struct daw ::json::json_data_contract<MyType> {
using type = json_member_list<json_array< " member_name " , std::string>>;
};
vcpkg を使用して最新リリースを取得できます。ポートはdaw-json-link
と呼ばれます
find_package ( daw-json-link )
#...
target_link_libraries ( MyTarget daw::daw-json-link )
このライブラリはヘッダーのみであり、その 2 つの依存関係とともにクローンを作成して、それぞれのinclude/
サブフォルダーをコンパイラーのインクルード パスに追加できます。
cmake プロジェクトで daw_json_link を使用するには、以下を追加すると、依存関係とともに daw_json_link を取り込めるようになります。
include ( FetchContent )
FetchContent_Declare(
daw_json_link
GIT_REPOSITORY https://github.com/beached/daw_json_link
GIT_TAG release
)
FetchContent_MakeAvailable(daw_json_link)
#...
target_link_libraries ( MyTarget daw::daw-json-link )
bash を備えたシステムでは、他のシステムでも同様で、以下をシステムにインストールできます。
git clone https://github.com/beached/daw_json_link
cd daw_json_link
mkdir build
cd build
cmake ..
cmake --install .
これにより、インストール プレフィックスのインクルード フォルダーがコンパイラーのインクルード パスに含まれている限り、cmake find_package のインストール、または通常のヘッダーとしての使用が可能になります。
以下はテストをビルドして実行します。
git clone https://github.com/beached/daw_json_link
cd daw_json_link
mkdir build
cd build
cmake -DDAW_ENABLE_TESTING=On ..
cmake --build .
ctest .
ビルド後、個々のサンプルをテストすることもできます。 city_test_bin
は都市の JSON ファイルへのパスが必要です。
./tests/city_test_bin ../test_data/cities.json
通常、データ構造内のメンバーの順序は、可能であれば JSON データの順序と一致する必要があります。値をバックトラックする必要がない場合、パーサーは高速になります。オプションの値が JSON データに欠落している場合も、解析が遅くなる可能性があります。可能であれば、null として送信してください。パーサーは割り当てを行いません。データ型に解析されると、そのデータ構造が割り当てを行うため、カスタム アロケーターまたは混合を使用できるようになります。配列のデフォルトでは std::vector が使用されます。これが望ましくない場合は、型を指定する必要があります。
現在、ライブラリはシリアル化時にメンバー名をエスケープ解除/エスケープしません。メンバー名は有効でエスケープされていないことが期待されます。これにはコストがかかるため、将来的にはオプションで追加される可能性があります。
C++17 と C++20 の間には若干の違いがあり、C++20 では C++17 では使用できない一部のコードが許可されます。
namespace daw ::json {
template <>
struct json_data_contract <MyType> {
static constexpr char const member_name[] = " memberName " ;
using type = json_member_list<json_number<member_name>>;
};
}
C++ のどちらのバージョンでも、メンバーに名前を付けるこの方法がサポートされています。
C++20 コンパイラ内でコンパイルする場合、C++17 のようにchar const *
渡すことに加えて、メンバー名を文字列リテラルとして直接指定できます。 C++20 コンパイラのサポートはまだ非常に初期段階にあり、ここにドラゴンがあります。 C++20 モードの g++9.x には既知の問題があり、g++10/11 でのみテストされています。ここにドラゴンがいる
namespace daw ::json {
template <>
struct json_data_contract <MyType> {
using type = json_member_list<json_number< " member_name " >>;
};
}
データ型がjson_data_contract
にマップされると、ライブラリは JSON を解析するメソッドを提供します。
MyClass my_class = from_json<MyClass>( json_str );
あるいは、入力が信頼できる場合は、チェックの少ないバージョンの方が高速になる可能性があります。
MyClass my_class = from_json<MyClass, options::parse_flags<options::CheckedParseMode::no>>( json_str );
配列ルートを持つ JSON ドキュメントは、 from_json_array
関数を使用して解析します
std::vector<MyClass> my_data = from_json_array<MyClass>( json_str );
あるいは、入力が信頼できる場合は、チェックの少ないバージョンの方が高速になる可能性があります。
std::vector<MyClass> my_data = from_json_array<MyClass, std::vector<MyClass>, options::parse_flags<options::CheckedParseMode::no>>( json_str );
json_array_iterator
JSON 配列データから作業したい場合は、反復子を取得し、標準アルゴリズムを使用して、JSON データ内の配列の反復処理をjson_array_iterator
経由で実行できます。
using iterator_t = json_array_iterator<MyClass>;
auto pos = std::find( iterator_t ( json_str ), iterator_t ( ), MyClass( ... ) );
あるいは、入力が信頼できる場合は、あまりチェックされていないバージョンを呼び出すこともできます。
using iterator_t = daw::json::json_array_iterator<MyClass, options::CheckedParseMode::no>;
auto pos = std::find( iterator_t ( json_str ), iterator_t ( ), MyClass( ... ) );
json_value
API のような DOM の場合、GUI やマッピングが不十分な場合のコードの提供などによく使用され、 json_value
使用できます。これは json_to_cpp ツールで使用されます。
auto jv = daw::json::json_value( json_doc );
JSON パスを使用して整数を抽出できます
int foo = as< int >( jv[ " path.to.int " ] );
ここで、 "path.to.int"
は、次のような JSON クラスへのドリルダウンを表す JSON パスです。
{
"path" : {
"to" : {
"int" : 5
}
}
}
JSON パスの構文のような配列を使用して選択することもできます。 "path[5]"
は"path"
の 5 番目の要素/メンバーを選択します。 JSON にシリアル化する場合。 JSON パス構文は、 from_json
、 from_json_array
、およびjson_array_iterator
でも機能します。
to_json
std::string my_json_data = to_json( MyClass{} );
または、配列、コレクション、範囲、またはビューをシリアル化します。この型で機能するにはstd::begin(...)
とstd::end(...)
のみが必要です。これにより、型が構築可能なコレクションではない場合でもシリアル化が可能になります。
std::vector<MyClass> arry = ...;
std::string my_json_data = to_json_array( arry );
解析エラーはデフォルトで、失敗の理由と場所に関する情報を含むdaw::json::json_exception
をスローします。
例外が無効になっている場合、ライブラリはデフォルトで解析エラーが発生したときにstd::terminate
呼び出します。
一方、エラー処理のデフォルトでは、エラーが発生した場合はdaw::json::json_exception
がスローされ、例外が無効になっている場合はstd::terminate
が呼び出されます。この動作は、関数ポインターdaw::json::daw_json_error_handler
を設定することで変更できます。唯一の要件は、関数が戻らないことです。これを利用する例は、error_handling_bench_test.cpp にあります。
エラー チェックは解析ごとに変更できます。 from_json
、 from_json_array
、 json_value
、 json_array_iterator
などはすべて解析オプションをサポートしています。呼び出しにはパーサー オプションを指定できます。使用可能なオプションは、parser_policies クックブック項目に記載されています。
daw::json::json_exception
にはメンバー関数std::string_view reason( ) const
がありstd::exception
のwhat( )
に似ていますが、 what( )
よりも多くのコンテキストを含むstd::string
返します。例外が存在する環境で例外を無効にしたい場合は、 DAW_JSON_DONT_USE_EXCEPTIONS
定義してライブラリによる例外スローを無効にするか、ハンドラーを設定できます。ハンドラーは 2 つのデフォルトのdaw::json::default_error_handling_throwing
のいずれかに設定できるため、これは推奨されなくなりました。 daw::json::default_error_handling_throwing
またはdaw::json::default_error_handling_terminating
。
これはdaw::json
名前空間にjson_data_contract
の特殊化を記述することで実現できます。例えば:
# include < daw/json/daw_json_link.h >
# include < string >
# include < string_view >
# include < vector >
struct TestClass {
int i = 0 ;
double d = 0.0 ;
bool b = false ;
std::string s{};
std::vector< int > y{};
TestClass ( int Int, double Double, bool Bool, std::string S,
std::vector< int > Y)
: i(Int), d(Double), b(Bool), s(std::move( S ) ), y(std::move( Y )) {}
};
namespace daw ::json {
template <>
struct json_data_contract <TestClass> {
using type =
json_member_list<
json_number< " i " , int >,
json_number< " d " >,
json_bool< " b " >,
json_string< " s " >,
json_array< " y " , int >
>;
};
} // namespace daw::json
int main () {
std::string_view test_001_t_json_data = R"( {
"i":5,
"d":2.2e4,
"b":false,
"s":"hello world",
"y":[1,2,3,4]
} )" ;
std::string_view json_array_data = R"( [{
"i":5,
"d":2.2e4,
"b":false,
"s":"hello world",
"y":[1,2,3,4]
},{
"i":4,
"d":122e4,
"b":true,
"s":"goodbye world",
"y":[4,3,1,4]
}] )" ;
TestClass test_class = daw::json::from_json<TestClass>(test_001_t_json_data);
std::vector<TestClass> arry_of_test_class =
daw::json::from_json_array<TestClass>(test_001_t_json_data);
}
コンパイラエクスプローラーで見る
集約コンストラクターとユーザー コンストラクターの両方がサポートされています。説明では、型と順序を構築するために必要な値が提供されます。指定された順序は、コンストラクターに配置される順序です。型を構築する方法を提供するカスタマイズ ポイントもあります。次のようなクラス:
# include < daw/json/daw_json_link.h >
struct AggClass {
int a{};
double b{};
};
namespace daw ::json {
template <>
struct json_data_contract <AggClass> {
using type = json_member_list<
json_number< " a " , int >,
json_number< " b " >
>;
};
}
動作もします。同じですが C++17
# include < daw/json/daw_json_link.h >
struct AggClass {
int a{};
double b{};
};
namespace daw ::json {
template <>
struct json_data_contract <AggClass> {
static inline constexpr char const a[] = " a " ;
static inline constexpr char const b[] = " b " ;
using type = json_member_list<
json_number<a, int >,
json_number<b>
>;
};
}
クラスの説明はそのサブメンバーとともに再帰的です。以前のAggClass
使用すると、それを別のクラスのメンバーとして含めることができます
// See above for AggClass
struct MyClass {
AggClass other;
std::string_view some_name;
};
namespace daw ::json {
template <>
struct json_data_contract <MyClass> {
using type = json_member_list<
json_class< " other " , AggClass>,
json_string< " id " , std::string_view>
>;
};
}
上記は、AggClass と記述された別のクラスを持つクラスMyClass
をマップします。また、C++ クラスのメンバー名はマップされた JSON 名のメンバー名と一致する必要がなく、文字列は結果の型としてstd::string_view
使用できることがわかります。 JSON ファイルを含むバッファーがクラスが存在する限り存在することを保証できる場合、これは重要なパフォーマンスの向上です。
JSON 配列を反復処理します。入力反復子daw::json::json_array_iterator<JsonElement>
すると、JSON 要素の配列を反復処理できます。これは技術的には入力イテレータですが、前方イテレータと同様に保存して再利用できます。参照ではなく値を返します。
# include < daw/json/daw_json_link.h >
# include < daw/json/daw_json_iterator.h >
# include < iostream >
struct AggClass {
int a{};
double b{};
};
namespace daw ::json {
template <>
struct json_data_contract <AggClass> {
using type = json_member_list<
json_number< " a " , int >,
json_number< " b " >
>;
};
} // namespace daw::json
int main () {
std::string json_array_data = R"( [
{"a":5,"b":2.2},
{"a":5,"b":3.14},
{"a":5,"b":0.122e44},
{"a":5334,"b":34342.2}
] )" ;
using iterator_t = daw::json::json_array_iterator<AggClass>;
auto pos =
std::find_if (
iterator_t (json_array_data),
iterator_t (),
[](AggClass const &element) {
return element. b > 1000.0 ;
}
);
if (pos == iterator_t ()) {
std::cout << " Not found n " ;
} else {
std::cout << " Found n " ;
}
}
解析は特定のメンバーまたは要素から開始できます。 from_json
、 from_json_array
、 json_value
、 json_array_iterator
などへのオプションのメンバー パスを指定できます。形式は、ドットで区切られたメンバー名のリストであり、オプションでmember0.member1
などの配列インデックスも指定できます。これは、次の解析に似ています。
{
"member0" : {
"member1" : {}
}
}
またはmember0[5].member1
、次のようなドキュメント内の "member1" で解析を開始します。
{
"member0" : [
" a " ,
" b " ,
" c " ,
" d " ,
" e " ,
{
"member1" : " "
}
]
}
または
{
"member0" : {
"a" : " " ,
"b" : " " ,
"c" : " " ,
"d" : " " ,
"e" : " " ,
"f" : {
"member1" : " "
}
}
}
コメントは、そのパーサー ポリシーが使用されている場合にサポートされます。現在、コメント ポリシーには 2 つの形式があります。
//
行コメントと C スタイルの/* */
コメント。 { // This is a comment
"a" /*this is also a comment*/: "a's value"
}
#
行コメント { # This is a comment
"a" #this is also a comment
: "a's value"
}
コメントポリシーは、 PolicyCommentTypes
を介して設定できます。詳細については、「parser_policies」を参照してください。
シリアル化を有効にするにはjson_data_contract
の特殊化にto_json_data( Thing const & );
メンバーのタプルを返します。これにより、型からクラスの説明で指定された引数へのマッピングが提供されます。 JSON 文字列にシリアル化するには、 to_json( my_thing );
ここで、 my_thing
は登録された型、またはコンテナ、マップ、文字列、ブール、数値などの基本的な型の 1 つです。 to_json_data( Thing const & )
静的メソッドの結果は、要素が付随するjson_data_contract
型エイリアスtype
の順序と一致するtuple
です。このメソッドの使用方法が原因で、右辺値要素を含むタプルでは、破壊後の使用に関するバグが発生します。これが発生するとコンパイラはエラーになります。 <daw/daw_tuple_forward.h>
とメソッドdaw::forward_nonrvalue_as_tuple
を含めると、参照渡しではなく右辺値が保存されます。多くの場合、それは計算されたタプル要素の結果です。上記の例を使用して、 to_json_data
メソッドを追加してみましょう
# include < daw/json/daw_json_link.h >
# include < tuple >
struct AggClass {
int a;
double b;
};
namespace daw ::json {
template <>
struct json_data_contract <AggClass> {
using type = json_member_list<
json_number< " a " , int >,
json_number< " b " >
>;
static constexpr auto to_json_data ( AggClass const & value ) {
return std::forward_as_tuple ( value. a , value. b );
}
};
}
// ...
AggData value = // ...;
std::string test_001_t_json_data = to_json( value );
// or
std::vector<AggData> values = // ...;
std::string json_array_data = to_json_array( values );
あるいは、任意の WritableOutput タイプに出力することもできます。デフォルトでは、これには FILE*、iostream、Character のコンテナ、および Character ポインタが含まれます。型のjson_data_constract
内。または、オプトインした場合は、 opt_into_iostreams
という名前の型エイリアスを追加することで、出力ストリームに json を挿入する型の ostream 演算子<< を取得できます。エイリアスの型は関係なく、 daw/json/daw_json_iostream.h
をインクルードします。 。例えば
# include < daw/json/daw_json_link.h >
# include < daw/json/daw_json_iostream.h >
# include < tuple >
struct AggClass {
int a{};
double b{};
};
namespace daw ::json {
template <>
struct json_data_contract <AggClass> {
using opt_into_iostreams = void ;
using type = json_member_list<
json_number< " a " , int >,
json_number< " b " >
>;
static inline auto to_json_data ( AggClass const & value ) {
return std::forward_as_tuple ( value. a , value. b );
}
};
}
// ...
AggData value = // ...;
std::cout << value << ' n ' ;
// or
std::vector<AggData> values = // ...;
std::cout << values << ' n ' ;
実際の例は、daw_json_iostream_test.cpp またはコンパイラー エクスプローラーにあります。
error: pointer to subobject of string literal is not allowed in a template argument
constexpr char const member_name[] = " member_name " ;
// ...
json_link<member_name, Type>
JSON リンクの動作に影響を与える定義がいくつかあります。
DAW_JSON_DONT_USE_EXCEPTIONS
- 例外を許可するかどうかを制御します。そうでない場合は、エラー時にstd::terminate()
発生します。例外が無効になっている場合 (例-fno-exceptions
)、これは自動的に行われます。DAW_ALLOW_SSE42
- 実験的な SSE42 モードを許可します。一般に constexpr モードの方が高速です。DAW_JSON_NO_CONST_EXPR
- これを使用すると、C++ 20 より前の JSON データから移動/コピー特殊メンバーのないクラスを構築できるようになります。このモードは、このフラグが必要でなくなった C++20 より前の定数式では機能しません。 古いコンパイラはまだ動作する可能性がありますが、テストではバグのある C++17 サポートにより ICE またはコンパイル エラーが発生するものもありました。多くの場合、constexpr を使用しないことも役に立ちます。
json_key_value
解析タイプを使用することで、両方を解析または順序付けできることを保証できます。std::multimap<std::string, T>
またはstd::vector<std::pair<std::string, T>>
とともに使用すると、すべてのメンバーが前者の順序で保持されます。あるいは、 json_value
タイプを使用すると、クラス メンバーの反復と正しいメンバーの遅延解析が可能になります。これらの方法を説明するクックブックのキー値を参照してください。