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;
}

参考资料

相关推荐
scx201310046 小时前
20260105 莫队总结
c++
Q741_1476 小时前
海致星图招聘 数据库内核研发实习生 一轮笔试 总结复盘(1) 作答语言:C/C++ 链表 二叉树
开发语言·c++·经验分享·面试·笔试
咔咔咔的6 小时前
1970. 你能穿过矩阵的最后一天
c++
秃了也弱了。6 小时前
FASTJSON库:阿里出品java界json解析库,使用与踩坑记录
java·开发语言·json
_OP_CHEN6 小时前
【从零开始的Qt开发指南】(十九)Qt 文件操作:从 I/O 设备到文件信息,一站式掌握跨平台文件处理
开发语言·c++·qt·前端开发·文件操作·gui开发·qt文件
CSDN_RTKLIB6 小时前
【std::map】双向迭代器说明
c++·stl
王老师青少年编程6 小时前
信奥赛C++提高组csp-s之欧拉回路
c++·算法·csp·欧拉回路·信奥赛·csp-s·提高组
No0d1es6 小时前
2025年12月 GESP CCF编程能力等级认证C++六级真题
c++·青少年编程·gesp·ccf·6级
Terrence Shen6 小时前
【CUDA编程系列】之01
c++·人工智能·深度学习·机器学习
墨有6666 小时前
数学分析栈的出栈顺序:从算法判断到数学本质(卡特兰数初探)
c++·算法·数学建模