
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_jsonstd::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++20 之前的常量表达式中不起作用。 较旧的编译器可能仍然可以工作,但在测试中,由于 C++17 支持存在缺陷,某些编译器会导致 ICE 或编译错误。通常不使用 constexpr 也有帮助。
json_key_value解析类型可以保证两者都可以被解析或排序。std::multimap<std::string, T>或std::vector<std::pair<std::string, T>>一起使用时,所有成员均按前者的顺序保留。或者, json_value类型将允许对类成员进行迭代并延迟解析正确的成员。请参阅演示这些方法的 Cookbook Key Values。