JSON Link 函式庫是一個高效能、無分配的 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 文件和一個事件處理程序。事件處理程序可以透過具有以下任何成員來選擇事件:
將類別映射到 JSON 文件是透過專門化特徵daw::json::json_data_contract
來完成的。如果已對應的類別是另一個映射類別的成員,則無需再次對應。特徵json_data_contract
有兩個部分,第一個是名為type
的型別別名,它將 JSON 成員對應到我們類別的建構子。這可以避免需要對類別進行私有訪問,假設構造類別也需要我們序列化的資料。例如:
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 類別將至少有兩個成員“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
的變體元素列表,可以使用一些本機 C++ 資料類型而不是 JSON 映射類型。這包括整數、浮點、布林、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 )
該庫只是頭文件,可以克隆,以及它的兩個依賴項,然後將每個依賴項的include/
子資料夾添加到編譯器的包含路徑中
要在 cmake 專案中使用 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 資料中缺少可選值時,也會減慢解析速度。如果可能的話,將它們作為空發送。解析器不分配。解析為資料類型可能會允許人們使用自訂分配器或混合分配器,因為它們的資料結構將進行分配。陣列的預設值是使用 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 數組數據,你可以取得一個迭代器並使用 std 演算法來迭代 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 Path 來擷取整數
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
來停用程式庫拋出異常或設定處理程序,不再建議這樣做,因為處理程序可以設定為兩個預設值之一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>
>;
};
}
上面對應了一個類別MyClass
,該類別具有另一個被描述為 AggClass 的類別。此外,您還可以看到 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" : " "
}
}
}
當使用註解的解析器策略時支援註解。目前,評論政策有兩種形式。
//
行註解與 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
是註冊類型或基本類型之一,例如容器、映射、字串、布林值和數字。 to_json_data( Thing const & )
靜態方法的結果是一個tuple
,其元素與隨附的json_data_contract
類型別名type
中的順序相符。由於方法的使用方式,具有右值元素的元組將導致銷毀後使用錯誤。如果發生這種情況,編譯器會出錯。包含<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、字元容器和字元指標。在您類型的json_data_constract
中。或者,如果選擇加入,則可以為其類型取得一個 ostream 運算子<<,透過新增名為opt_into_iostreams
型別別名(它的別名型別無關緊要)將 json 插入輸出流中,並包含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 Link 的運作方式
DAW_JSON_DONT_USE_EXCEPTIONS
- 控制是否允許異常。如果不是,則會發生std::terminate()
錯誤。如果停用異常(例如-fno-exceptions
),這是自動的DAW_ALLOW_SSE42
- 允許實驗性 SSE42 模式,通常 constexpr 模式更快DAW_JSON_NO_CONST_EXPR
- 這可用於允許從 C++ 20 之前的 JSON 資料構造沒有移動/複製特殊成員的類別。 較舊的編譯器可能仍然可以工作,但在測試中,由於 C++17 支援有缺陷,某些編譯器會導致 ICE 或編譯錯誤。通常不使用 constexpr 也有幫助。
json_key_value
解析類型可以保證兩者都可以被解析或排序。std::multimap<std::string, T>
或std::vector<std::pair<std::string, T>>
一起使用時,所有成員均按前者的順序保留。或者, json_value
類型將允許對類別成員進行迭代並延遲解析正確的成員。請參閱示範這些方法的 Cookbook Key Values。