前言

C++ 中有许多第三方库可以解析 JSON,但是都不是很好用,不能像 Java 中的 gsonfastjson 一样直接把 JSON 直接转成对象。

找了许多库,找到了nlohmann/json: JSON for Modern C++使用起来比较方便,也踩了坑,来了解一下。

JSON 转对象

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#include <string>
#include "nlohmann/json.hpp"
struct Person
{
    std::string name;
    int age;
    NLOHMANN_DEFINE_TYPE_INTRUSIVE(Person, name, age)
};

std::string data = R"({"name":"json", "age": 18})"_json;
nlohmann::json j = nlohmann::json::parse(data.c_str());
auto p = j.get<Person>();

nlohmann/json 中,如果需要一个对象可以被解析需要实现 to_jsonfrom_json 这两个方法。如果每个类都要写一遍那就很麻烦,所以提供了 NLOHMANN_DEFINE_TYPE_INTRUSIVE 这个宏来简化代码,但是这里有个坑,下文会说。

有了这个准备就可以使用 json::parseJSON 字符串转成 json 对象,然后实用 json.get<T> 来把 JSON 转成对应的对象,这里需要传入要转换的模板。

对象转 JSON

1
2
3
4
5
Pserson p = {"json", 20};

nlohmann::json j = p;
std::string json = j.dump();
std::cout << json << std::endl;

要把对象转 JSON 字符串,可以通过 json.dump() 把对象转字符串。

处理没有 key 的情况

上面这样使用 NLOHMANN_DEFINE_TYPE_INTRUSIVE 宏来生成 to_jsonfrom_json 方法非常方便,但是却有坑,它不能处理 key 不存在的情况,会直接抛异常。

这时候我们就需要自定义转换的逻辑了,也就是要写 to_jsonfrom_json 两个方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct Person
{
    std::string name;
    int age;
};

void to_json(nlohmann::json& j, const Person& p) {
    j = nlohmann::json{{"name", p.name}, {"age", p.age}};
}

void from_json(const nlohmann::json& j, Person& p) {
    try {
        if (j.contains("name")) {
            j.at("name").get_to(p.name);
        }
        if (j.contains("age")) {
            j.at("age").get_to(p.age);
        }
    } catch(const nlohmann::json::exception& e) {
        std::cout << e.what() << std::endl;
    }
}

我们在 from_json 中对每个 key 先判断存不存在,存在才去获取 value ,这样就不会说 key 找不到了。

总结

C++ 中使用nlohmann/json: JSON for Modern C++来解析 JSON 相对其它库还是比较方便的,不用每次使用的时候都手动解析,但是和其它语言里的相比还是差了不少。

这里需要注意使用 NLOHMANN_DEFINE_TYPE_INTRUSIVE 的时候, key 如果不存在会直接抛异常。

解决这个问题可以自定义 from_jsonto_json 来进行灵活的控制。

参考