前言 C++ nlohmann/json 库是一个非常易用,高性能的 json 库。
CMake FetchContent方式集成 1
2
3
4
5
6
include ( FetchContent )
FetchContent_Declare ( json
URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz )
FetchContent_MakeAvailable ( json )
target_link_libraries ( json_demo PRIVATE nlohmann_json::nlohmann_json )
Copy 快速使用 包含头文件以及声明命名空间别名:
1
2
#include <nlohmann/json.hpp>
using json = nlohmann :: json ;
Copy 解析json 从文件读取解析 1
2
3
4
5
6
7
8
// method 1
std :: ifstream f ( "example.json" );
json data = json :: parse ( f );
// method 2
std :: ifstream i ( "file.json" );
json j ;
i >> j ;
Copy 从字符串读取解析 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 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 },
};
Copy 遍历 1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 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 " ;
}
Copy 查找 Key 1
2
3
4
5
if ( j . contains ( "key" )) {
}
if ( j . find ( "foo" ) != o . end ()) {
}
Copy 读取 key 1
2
3
4
5
6
7
8
auto value = j [ "key" ];
auto value = j . at ( "key" );
std :: string value = j [ "key" ]. template get < std :: string > ();
// C++17
using namespace std :: literals ;
// 如果key不存在,则返回默认值0
int v_integer = j . value ( "integer" sv , 0 );
Copy 删除 key 1
2
// delete an entry
o . erase ( "foo" );
Copy 注意:数组可能无法删除单个元素
写入 json 文件 1
2
std :: ofstream o ( "pretty.json" );
o << std :: setw ( 4 ) << j << std :: endl ;
Copy 序列化设置缩进 1
2
// 按照四个空格缩进打印json
std :: cout << j . dump ( 4 ) << std :: endl ;
Copy 任意类型转换 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using json = nlohmann :: json ;
namespace ns {
struct person {
std :: string name ;
std :: string address ;
int age ;
};
void to_json ( json & j , const person & p ) {
j = json {{ "name" , p . name }, { "address" , p . address }, { "age" , p . age }};
}
void from_json ( const json & j , person & p ) {
j . at ( "name" ). get_to ( p . name );
j . at ( "address" ). get_to ( p . address );
j . at ( "age" ). get_to ( p . age );
}
} // namespace ns
// create a person
ns :: person p { "Ned Flanders" , "744 Evergreen Terrace" , 60 };
// conversion: person -> json
json j = p ;
std :: cout << j << std :: endl ;
// {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"}
// conversion: json -> person
auto p2 = j . template get < ns :: person > ();
Copy 通过实现to_json()
和from_json()
完成 json 与结构体的映射关系。
[!warning]
使用template get<your_type>()
时,必须保证是可以默认构造的。 from_json
中,使用 at()
来访问对象的值,而不是 operator[]
。如果某个键不存在, at
会抛出一个可以处理的异常,而 operator[]
则会表现出未定义的行为。如果想要将 JSON 对象用作序列化或想要将成员变量名称用作该对象的键。可以使用宏简化操作(自动实现to_json()
和from_json()
):
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(name, member1, member2, ...)
:在类/结构体的命名空间内进行定义。NLOHMANN_DEFINE_TYPE_INTRUSIVE(name, member1, member2, ...)
:在类/结构内部定义的标识符,用于创建代码。这个宏还可以访问私有成员。命名空间中使用:
1
2
3
4
5
6
7
8
namespace ns {
struct person {
std :: string name ;
std :: string address ;
int age ;
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE ( person , name , address , age )
}
Copy 使用私有成员:
1
2
3
4
5
6
7
8
9
10
11
namespace ns {
class address {
private :
std :: string street ;
int housenumber ;
int postcode ;
public :
NLOHMANN_DEFINE_TYPE_INTRUSIVE ( address , street , housenumber , postcode )
};
}
Copy 宏相当于:
1
2
3
4
5
6
7
8
9
10
11
12
13
void to_json ( nlohmann :: json & nlohmann_json_j , const person & nlohmann_json_t )
{
nlohmann_json_j [ "name" ] = nlohmann_json_t . name ;
nlohmann_json_j [ "address" ] = nlohmann_json_t . address ;
nlohmann_json_j [ "age" ] = nlohmann_json_t . age ;
}
void from_json ( const nlohmann :: json & nlohmann_json_j , person & nlohmann_json_t )
{
nlohmann_json_t . name = nlohmann_json_j . at ( "name" );
nlohmann_json_t . address = nlohmann_json_j . at ( "address" );
nlohmann_json_t . age = nlohmann_json_j . at ( "age" );
}
Copy 注意:
使用宏时,如果 json 中缺少某个键,赋值给结构体时,则会抛出 out_of_range.403
异常。但是 json 中如果有多余的键,则会自动忽略。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// incomplete deserialization:
json j3 = R "({" address ": " 742 Evergreen Terrace ", " name ": " Maggie Simpson "})" _json ;
try
{
auto p3 = j3 . template get < ns :: person > ();
}
catch ( const json :: exception & e )
{
std :: cout << "deserialization failed: " << e . what () << std :: endl ;
}
// output:
// deserialization failed: [json.exception.out_of_range.403] key 'age' not found
// redundant fields
json j2 = R "({" address ": " 742 Evergreen Terrace ", " name ": " Maggie Simpson ", " age ": 20, " sex ": " man "})" _json ;
ns :: person p2 = j2 . template get < ns :: person > ();
Copy 目前 nlohmann 最新 Release 版本为 3.11.3,还未支持 std::optional
,所以当遇到 json 为 null 时,需要额外处理,通过重新定义宏的方式,处理 null 的情形。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifdef NLOHMANN_JSON_FROM
#pragma push_macro("NLOHMANN_JSON_FROM")
#undef NLOHMANN_JSON_FROM
#endif
// ignore null in json
#define NLOHMANN_JSON_FROM(v1) \
{ \
if (nlohmann_json_j.at(#v1).is_null()) { \
nlohmann_json_t.v1 = {}; \
} else { \
nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); \
} \
}
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE ( MakeupItem , id , name , order_no , status , makeup_key , created_at , updated_at ,
deleted_at , infos , colors )
#ifdef NLOHMANN_JSON_FROM
#pragma pop_macro("NLOHMANN_JSON_FROM")
#endif
Copy 总结 以上就是在使用 json 库时的常用场景。
https://github.com/nlohmann/json
https://github.com/nlohmann/json/tree/develop/docs/examples
https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/#examples
预览: