nlohmann::json库对象和json结构体转换的新方式

nlohmann::json库对象和json结构体转换的新方式

nlohmann::json库官方地址为:https://json.nlohmann.me/,Github源代码托管地址为:https://github.com/nlohmann/json

在nlohmann json库 3.9.0 版本之前,如果将自定义类或者结构体对象之间相关转换,需要自定义对应的to_jsonfrom_json这两个函数。但是从3.9.0版本以后有6个宏可以替我们完成这项事情,具体可以参考官方文档:

https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive

复制代码
NLOHMANN_DEFINE_TYPE_INTRUSIVE
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT
NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE

其中,前面3个宏是侵入式的,需要在类或结构体对象里面进行声明使用;

后面3个宏是非侵入式的,可以在类或结构体对象外部定义,但是需要和该类或结构体定义在同一个命名空间中。

当前实现最多支持 64 个成员变量。如果要序列化/反序列化包含超过 64 个成员变量的类型,则需要手动定义 to_json/from_json 函数。

6个宏的定义

1. NLOHMANN_DEFINE_TYPE_INTRUSIVE

cpp 复制代码
/*!
@brief macro
@def NLOHMANN_DEFINE_TYPE_INTRUSIVE
@since version 3.9.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...)  \
    template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
    friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
    template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
    friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }

2. NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT

cpp 复制代码
/*!
@brief macro
@def NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT
@since version 3.11.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...)  \
    template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
    friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
    template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
    friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }

3. NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE

cpp 复制代码
/*!
@brief macro
@def NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE
@since version 3.11.3
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, ...)  \
    template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
    friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }

4. NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE

cpp 复制代码
/*!
@brief macro
@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
@since version 3.9.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...)  \
    template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
    void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
    template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
    void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }

5. NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT

cpp 复制代码
/*!
@brief macro
@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT
@since version 3.11.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...)  \
    template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
    void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
    template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
    void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }

6. NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE

cpp 复制代码
/*!
@brief macro
@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE
@since version 3.11.3
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, ...)  \
    template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
    void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }

侵入式代码示例

主要涉及到NLOHMANN_DEFINE_TYPE_INTRUSIVE
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT
NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE

这3个宏

如果您希望使用 JSON 对象进行序列化,并希望将成员变量名用作该对象的键,则可以使用这些宏来简化类型的序列化/反序列化。该宏需要在要为其编写代码的类/结构体内部定义。与 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE 不同,它可以访问私有成员。第一个参数是类/结构体的名称,其余参数均为成员名称。

    1. 反序列化时将使用 at 参数,如果 JSON 对象中缺少某个键,则会抛出 out_of_range.403 异常。
    1. 反序列化时将使用 value 参数,如果 JSON 对象中缺少某个键,则会回退到成员变量相应类型的默认值。生成的 from_json() 函数默认会构造一个对象,并在调用 value 函数时使用其值作为默认值。
  • 3.仅定义序列化。当类型没有默认构造函数且仅需要序列化时,此宏非常有用。

默认定义

这些宏为类添加了两个友元函数,分别负责序列化和反序列化:

cpp 复制代码
template<typename BasicJsonType>
friend void to_json(BasicJsonType&, const type&);
template<typename BasicJsonType>
friend void from_json(const BasicJsonType&, type&); // except (3)

1. Example (1): NLOHMANN_DEFINE_TYPE_INTRUSIVE

请看以下完整示例:

cpp 复制代码
#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;
using namespace nlohmann::literals;

namespace ns
{
class person
{
  private:
    std::string name = "John Doe";
    std::string address = "123 Fake St";
    int age = -1;

  public:
    person() = default;
    person(std::string name_, std::string address_, int age_)
        : name(std::move(name_)), address(std::move(address_)), age(age_)
    {}

    NLOHMANN_DEFINE_TYPE_INTRUSIVE(person, name, address, age)
};
} // namespace ns

int main()
{
    ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};

    // serialization: person -> json
    json j = p;
    std::cout << "serialization: " << j << std::endl;

    // deserialization: json -> person
    json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json;
    auto p2 = j2.get<ns::person>();

    // incomplete deserialization:
    json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json;
    try
    {
        auto p3 = j3.get<ns::person>();
    }
    catch (const json::exception& e)
    {
        std::cout << "deserialization failed: " << e.what() << std::endl;
    }
}

输出:

bash 复制代码
serialization: {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"}
deserialization failed: [json.exception.out_of_range.403] key 'age' not found

注释

  • ns::person 是默认可构造的。这是使用该宏的必要条件。

  • ns::person 包含私有成员变量。因此,可以使用 NLOHMANN_DEFINE_TYPE_INTRUSIVE,但不能使用 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE。

  • 宏 NLOHMANN_DEFINE_TYPE_INTRUSIVE 在类内部使用。

  • 反序列化过程中如果缺少键"age",则会引发异常。要回退到默认值,可以使用 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT。

该宏等效于:

cpp 复制代码
#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;
using namespace nlohmann::literals;

namespace ns
{
class person
{
  private:
    std::string name = "John Doe";
    std::string address = "123 Fake St";
    int age = -1;

  public:
    person() = default;
    person(std::string name_, std::string address_, int age_)
        : name(std::move(name_)), address(std::move(address_)), age(age_)
    {}
	
	// NLOHMANN_DEFINE_TYPE_INTRUSIVE(person, name, address, age) 等价于下面两个友元函数 to_json 和 from_json
    template<typename BasicJsonType>
    friend void to_json(BasicJsonType& 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;
    }

    template<typename BasicJsonType>
    friend void from_json(const BasicJsonType& 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");
    }
};
} // namespace ns

int main()
{
    ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};

    // serialization: person -> json
    json j = p;
    std::cout << "serialization: " << j << std::endl;

    // deserialization: json -> person
    json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json;
    auto p2 = j2.get<ns::person>();

    // incomplete deserialization:
    json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json;
    try
    {
        auto p3 = j3.get<ns::person>();
    }
    catch (const json::exception& e)
    {
        std::cout << "deserialization failed: " << e.what() << std::endl;
    }
}

2. Example (2): NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT

请看以下完整示例:

cpp 复制代码
#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;
using namespace nlohmann::literals;

namespace ns
{
class person
{
  private:
    std::string name = "John Doe";
    std::string address = "123 Fake St";
    int age = -1;

  public:
    person() = default;
    person(std::string name_, std::string address_, int age_)
        : name(std::move(name_)), address(std::move(address_)), age(age_)
    {}

    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(person, name, address, age)
};
} // namespace ns

int main()
{
    ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};

    // serialization: person -> json
    json j = p;
    std::cout << "serialization: " << j << std::endl;

    // deserialization: json -> person
    json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json;
    auto p2 = j2.get<ns::person>();

    // incomplete deserialization:
    json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json;
    auto p3 = j3.get<ns::person>();
    std::cout << "roundtrip: " << json(p3) << std::endl;
}

输出:

cpp 复制代码
serialization: {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"}
roundtrip: {"address":"742 Evergreen Terrace","age":-1,"name":"Maggie Simpson"}

注释

  • ns::person 是默认可构造的。这是使用该宏的必要条件。

  • ns::person 包含私有成员变量。因此,NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT 宏适用,但 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT 宏不适用。

  • 宏 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT 在类内部使用。

  • 反序列化过程中缺少键"age"不会引发异常。而是使用默认值 -1。

该宏等效于:

cpp 复制代码
#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;
using namespace nlohmann::literals;

namespace ns
{
class person
{
  private:
    std::string name = "John Doe";
    std::string address = "123 Fake St";
    int age = -1;

  public:
    person() = default;
    person(std::string name_, std::string address_, int age_)
        : name(std::move(name_)), address(std::move(address_)), age(age_)
    {}

    // NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(person, name, address, age) 等效于下面2个友元函数 to_json 和 from_json
    template<typename BasicJsonType>
    friend void to_json(BasicJsonType& 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;
    }

    template<typename BasicJsonType>
    friend void from_json(const BasicJsonType& nlohmann_json_j, person& nlohmann_json_t)
    {
        person nlohmann_json_default_obj;
        nlohmann_json_t.name = nlohmann_json_j.value("name", nlohmann_json_default_obj.name);
        nlohmann_json_t.address = nlohmann_json_j.value("address", nlohmann_json_default_obj.address);
        nlohmann_json_t.age = nlohmann_json_j.value("age", nlohmann_json_default_obj.age);
    }
};
} // namespace ns

int main()
{
    ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};

    // serialization: person -> json
    json j = p;
    std::cout << "serialization: " << j << std::endl;

    // deserialization: json -> person
    json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json;
    auto p2 = j2.get<ns::person>();

    // incomplete deserialization:
    json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json;
    auto p3 = j3.get<ns::person>();
    std::cout << "roundtrip: " << json(p3) << std::endl;
}

请注意,from_json 中使用了默认初始化的 person 对象来填充缺失值。

3. Example (3): NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE

请看以下完整示例:

cpp 复制代码
#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;
using namespace nlohmann::literals;

namespace ns
{
class person
{
  private:
    std::string name = "John Doe";
    std::string address = "123 Fake St";
    int age = -1;

  public:
    // No default constructor
    person(std::string name_, std::string address_, int age_)
        : name(std::move(name_)), address(std::move(address_)), age(age_)
    {}

    NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(person, name, address, age)
};
} // namespace ns

int main()
{
    ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};

    // serialization: person -> json
    json j = p;
    std::cout << "serialization: " << j << std::endl;
}

输出:

bash 复制代码
serialization: {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"}

注释

  • ns::person 是不可默认构造的。因此,可以使用此宏代替 NLOHMANN_DEFINE_TYPE_INTRUSIVE 和 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT。

  • ns::person 具有私有成员变量。因此,可以使用 NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE,但不能使用 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE。

  • 宏 NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE 在类内部使用。

该宏等效于:

cpp 复制代码
#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;
using namespace nlohmann::literals;

namespace ns
{
class person
{
  private:
    std::string name = "John Doe";
    std::string address = "123 Fake St";
    int age = -1;

  public:
    // No default constructor
    person(std::string name_, std::string address_, int age_)
        : name(std::move(name_)), address(std::move(address_)), age(age_)
    {}

    template<typename BasicJsonType>
    friend void to_json(BasicJsonType& 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;
    }
};
} // namespace ns

int main()
{
    ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};

    // serialization: person -> json
    json j = p;
    std::cout << "serialization: " << j << std::endl;
}

非侵入式代码示例

主要涉及到NLOHMANN_DEFINE_TYPE_NON_INTRUSIVENLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULTNLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE

这3个宏

如果您希望使用 JSON 对象进行序列化,并希望将成员变量名用作该对象的键,则可以使用这些宏来简化类型的序列化/反序列化。该宏需要在要为其编写代码的类/结构体内部定义。与 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE 不同,它可以访问私有成员。第一个参数是类/结构体的名称,其余参数均为成员名称。

  • 1.反序列化时将使用 at 参数,如果 JSON 对象中缺少某个键,则会抛出 out_of_range.403 异常。

  • 2.反序列化时将使用 value 参数,如果 JSON 对象中缺少某个键,则会回退到成员变量相应类型的默认值。生成的 from_json() 函数默认会构造一个对象,并在调用 value 函数时使用其值作为默认值。

  • 3.仅定义序列化。当类型没有默认构造函数且仅需要序列化时,此宏非常有用。

1. Example (1): NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE

请看以下完整示例:

cpp 复制代码
#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;
using namespace nlohmann::literals;

namespace ns
{
struct person
{
    std::string name;
    std::string address;
    int age;
};

NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, address, age)
} // namespace ns

int main()
{
    ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};

    // serialization: person -> json
    json j = p;
    std::cout << "serialization: " << j << std::endl;

    // deserialization: json -> person
    json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json;
    auto p2 = j2.get<ns::person>();

    // incomplete deserialization:
    json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json;
    try
    {
        auto p3 = j3.get<ns::person>();
    }
    catch (const json::exception& e)
    {
        std::cout << "deserialization failed: " << e.what() << std::endl;
    }
}

输出:

bash 复制代码
serialization: {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"}
deserialization failed: [json.exception.out_of_range.403] key 'age' not found

注释

  • ns::person 是默认可构造的。这是使用该宏的必要条件。

  • ns::person 只有公共成员变量。因此,可以使用 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE。

  • 宏 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE 在类外部、命名空间 ns 内使用。

  • 反序列化过程中如果缺少键"age",则会引发异常。要回退到默认值,可以使用 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT。

该宏等效于:

cpp 复制代码
#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;
using namespace nlohmann::literals;

namespace ns
{
struct person
{
    std::string name;
    std::string address;
    int age;
};

// NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, address, age) 宏
// 展开后等价于下面to_json 和 from_json 这两个模版函数
template<typename BasicJsonType>
void to_json(BasicJsonType& 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;
}

template<typename BasicJsonType>
void from_json(const BasicJsonType& 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");
}
} // namespace ns

int main()
{
    ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};

    // serialization: person -> json
    json j = p;
    std::cout << "serialization: " << j << std::endl;

    // deserialization: json -> person
    json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json;
    auto p2 = j2.get<ns::person>();

    // incomplete deserialization:
    json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json;
    try
    {
        auto p3 = j3.get<ns::person>();
    }
    catch (const json::exception& e)
    {
        std::cout << "deserialization failed: " << e.what() << std::endl;
    }
}

Example (2): NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT

请看以下完整示例:

cpp 复制代码
#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;
using namespace nlohmann::literals;

namespace ns
{
struct person
{
    std::string name = "John Doe";
    std::string address = "123 Fake St";
    int age = -1;

    person() = default;
    person(std::string name_, std::string address_, int age_)
        : name(std::move(name_)), address(std::move(address_)), age(age_)
    {}
};

NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(person, name, address, age)
} // namespace ns

int main()
{
    ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};

    // serialization: person -> json
    json j = p;
    std::cout << "serialization: " << j << std::endl;

    // deserialization: json -> person
    json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json;
    auto p2 = j2.get<ns::person>();

    // incomplete deserialization:
    json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json;
    auto p3 = j3.get<ns::person>();
    std::cout << "roundtrip: " << json(p3) << std::endl;
}

输出

bash 复制代码
serialization: {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"}
roundtrip: {"address":"742 Evergreen Terrace","age":-1,"name":"Maggie Simpson"}

注释

  • ns::person 是默认可构造的。这是使用该宏的必要条件。

  • ns::person 只有公共成员变量。因此,宏 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT 适用。

  • NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT 在类外部、命名空间 ns 内使用。

  • 反序列化过程中缺少键"age"不会引发异常。而是使用默认值 -1。

该宏等效于:

cpp 复制代码
#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;
using namespace nlohmann::literals;

namespace ns
{
struct person
{
    std::string name = "John Doe";
    std::string address = "123 Fake St";
    int age = -1;

    person() = default;
    person(std::string name_, std::string address_, int age_)
        : name(std::move(name_)), address(std::move(address_)), age(age_)
    {}
};

// NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(person, name, address, age) 宏
// 展开后等价于下面to_json 和 from_json 这两个模版函数
template<typename BasicJsonType>
void to_json(BasicJsonType& 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;
}

template<typename BasicJsonType>
void from_json(const BasicJsonType& nlohmann_json_j, person& nlohmann_json_t)
{
    person nlohmann_json_default_obj;
    nlohmann_json_t.name = nlohmann_json_j.value("name", nlohmann_json_default_obj.name);
    nlohmann_json_t.address = nlohmann_json_j.value("address", nlohmann_json_default_obj.address);
    nlohmann_json_t.age = nlohmann_json_j.value("age", nlohmann_json_default_obj.age);
}
} // namespace ns

int main()
{
    ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};

    // serialization: person -> json
    json j = p;
    std::cout << "serialization: " << j << std::endl;

    // deserialization: json -> person
    json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json;
    auto p2 = j2.get<ns::person>();

    // incomplete deserialization:
    json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json;
    auto p3 = j3.get<ns::person>();
    std::cout << "roundtrip: " << json(p3) << std::endl;
}

请注意,from_json 中使用了默认初始化的 person 对象来填充缺失值。

Example (3): NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE

请看以下完整示例:

cpp 复制代码
#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;
using namespace nlohmann::literals;

namespace ns
{
struct person
{
    std::string name;
    std::string address;
    int age;
};

NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(person, name, address, age)
} // namespace ns

int main()
{
    ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};

    // serialization: person -> json
    json j = p;
    std::cout << "serialization: " << j << std::endl;
}

输出:

cpp 复制代码
serialization: {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"}

注释

  • ns::person 是不可默认构造的。因此,可以使用此宏代替 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE 和 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT。

  • ns::person 只有公共成员变量。因此,可以使用 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE。

该宏等效于:

cpp 复制代码
#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;
using namespace nlohmann::literals;

namespace ns
{
struct person
{
    std::string name;
    std::string address;
    int age;
};

// NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(person, name, address, age) 等价于下面 to_json 这个模版函数
template<typename BasicJsonType>
void to_json(BasicJsonType& 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;
}
} // namespace ns

int main()
{
    ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};

    // serialization: person -> json
    json j = p;
    std::cout << "serialization: " << j << std::endl;
}

参考资料

相关推荐
浔川python社2 小时前
C++小程序编写系列(2)
c++·算法·图论
fengche19152 小时前
【无标题】keilC编译器版本问题,低版本
c++
Chen--Xing2 小时前
LeetCode 15.三数之和
c++·python·算法·leetcode·rust
fantasy5_52 小时前
C++ 智能指针深度解析:原理、实现与实战避坑
java·开发语言·c++
神仙别闹12 小时前
基于QT(C++)实现学本科教务系统(URP系统)
数据库·c++·qt
生产队队长12 小时前
Web:免费的JSON接口
json
deng-c-f12 小时前
Linux C/C++ 学习日记(49):线程池
c++·学习·线程池
ulias21212 小时前
C++ 的容器适配器——从stack/queue看
开发语言·c++
daidaidaiyu13 小时前
FFmpeg 关键的结构体
c++·ffmpeg