3. nlohmann/json入门

nlohmann/json是一个开源的C++库,用于处理JSON数据。它提供了简单、直观的API,使得在C++中解析、创建和操作JSON数据变得非常方便。

这部分将会介绍nlohmann/json的基本用法,包括如何解析JSON数据、如何创建JSON数据、如何访问JSON数据、如何修改JSON数据、如何序列化JSON数据等。

3.1. 如何从文件中读取json数据

1ifstream f("example.json");
2nlohmann::json data = nlohmann::json::parse(f);
3/* nlohmann::json data;
4data >> f;*/

3.2. json作为第一类数据类型

以下是一些示例,可帮助您了解如何使用该类。

假设您要创建 JSON 对象:

 1nlohmann::json data = {
 2    {"pi", 3.141},
 3    {"happy", true},
 4    {"name", "Niels"},
 5    {"nothing", nullptr},
 6    {"answer", {
 7        {"everything", 42}
 8    }},
 9    {"list", {1, 0, 2}},
10    {"object", {
11        {"currency", "USD"},
12        {"value", 42.99}
13    }}
14};

有了这个库,你可以写:

 1// create an empty structure (null)
 2json j;
 3// add a number that is stored as double (note the implicit conversion of j to an object)
 4j["pi"] = 3.141;
 5// add a Boolean that is stored as bool
 6j["happy"] = true;
 7// add a string that is stored as std::string
 8j["name"] = "Niels";
 9// add another null object by passing nullptr
10j["nothing"] = nullptr;
11// add an object inside the object
12j["answer"]["everything"] = 42;
13// add an array that is stored as std::vector (using an initializer list)
14j["list"] = { 1, 0, 2 };
15// add another object (using an initializer list of pairs)
16j["object"] = { {"currency", "USD"}, {"value", 42.99} };
17// instead, you could also write (which looks very similar to the JSON above)
18json j2 = {
19    {"pi", 3.141},
20    {"happy", true},
21    {"name", "Niels"},
22    {"nothing", nullptr},
23    {"answer", {
24        {"everything", 42}
25    }},
26    {"list", {1, 0, 2}},
27    {"object", {
28        {"currency", "USD"},
29        {"value", 42.99}
30    }}
31};

请注意,在所有这些情况下,您永远不需要“告诉”编译器您要使用哪种 JSON 值类型。 如果您想明确或表达一些边缘情况,函数 json::array()json::object() 将有所帮助:

1// a way to express the empty array []
2json empty_array_explicit = json::array();
3// ways to express the empty object {}
4json empty_object_implicit = json({});
5json empty_object_explicit = json::object();
6// a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]]
7json array_not_object = json::array({ {"currency", "USD"}, {"value", 42.99} });

3.3. 序列化json对象

您可以将json对象序列化为字符串,一般的常见std类型均可直接使用括号赋值,例如:

 1std::vector<int> c_vector {1, 2, 3, 4};
 2json j_vec(c_vector);
 3// [1, 2, 3, 4]
 4std::deque<double> c_deque {1.2, 2.3, 3.4, 5.6};
 5json j_deque(c_deque);
 6// [1.2, 2.3, 3.4, 5.6]
 7std::list<bool> c_list {true, true, false, true};
 8json j_list(c_list);
 9// [true, true, false, true]
10std::forward_list<int64_t> c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543};
11json j_flist(c_flist);
12// [12345678909876, 23456789098765, 34567890987654, 45678909876543]
13std::array<unsigned long, 4> c_array {{1, 2, 3, 4}};
14json j_array(c_array);
15// [1, 2, 3, 4]
16std::set<std::string> c_set {"one", "two", "three", "four", "one"};
17json j_set(c_set); // only one entry for "one" is used
18// ["four", "one", "three", "two"]
19std::unordered_set<std::string> c_uset {"one", "two", "three", "four", "one"};
20json j_uset(c_uset); // only one entry for "one" is used
21// maybe ["two", "three", "four", "one"]
22std::multiset<std::string> c_mset {"one", "two", "one", "four"};
23json j_mset(c_mset); // both entries for "one" are used
24// maybe ["one", "two", "one", "four"]
25std::unordered_multiset<std::string> c_umset {"one", "two", "one", "four"};
26json j_umset(c_umset); // both entries for "one" are used
27// maybe ["one", "two", "one", "four"]

但是显然,我们还会需要自定义一些类型,比如:

1struct MyStruct {
2    int i;
3    std::string str;
4    bool b;
5};

这时候,你觉得应该怎么做?如果你觉得要这样:

1json j;
2MyStruct my_struct;
3j["i"] = my_struct.i;
4j["str"] = my_struct.str;
5j["b"] = my_struct.b;
6ofstream outputFile("data.json");
7outputFile << j.dump(4);
8outputFile.close();

那就有点脱裤子放屁了,失去了“序列化”的最重要意义——简洁表述。

官方给我们定义了一个宏 NLOHMANN_DEFINE_TYPE_INTRUSIVE(name, member1, member2, ...) ,用于自定义类型的序列化,我们可以这样使用:

 1struct MyStruct {
 2    int i;
 3    std::string str;
 4    bool b;
 5    NLOHMANN_DEFINE_TYPE_INTRUSIVE(MyStruct, i, str, b)
 6};
 7json j;
 8MyStruct my_struct;
 9j = my_struct;
10ofstream outputFile("data.json");
11outputFile << j.dump(4);
12outputFile.close();

这样就可以自动序列化了,是不是很简洁?

3.4. 反序列化json对象

反序列化也是一样的,我们可以这样使用:

1json j = R"(
2{
3    "happy": true,
4    "pi": 3.141
5}
6)"_json;
7bool b = j.at("happy");
8double pi = j.at("pi");

对于自定义的类型,我们可以这样使用:

 1struct MyStruct {
 2    int i;
 3    std::string str;
 4    bool b;
 5    NLOHMANN_DEFINE_TYPE_INTRUSIVE(MyStruct, i, str, b)
 6};
 7json j = R"(
 8{
 9    "i": 1,
10    "str": "hello",
11    "b": true
12}
13)"_json;
14MyStruct my_struct = j;