Skip to content

NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE, NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT

#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(type, member...)              // (1)
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(type, member...) // (2)

These macros can be used to simplify the serialization/deserialization of types if you want to use a JSON object as serialization and want to use the member variable names as object keys in that object. The macro is to be defined outside the class/struct to create code for, but inside its namespace. Unlike NLOHMANN_DEFINE_TYPE_INTRUSIVE, it cannot access private members. The first parameter is the name of the class/struct, and all remaining parameters name the members.

  1. Will use at during deserialization and will throw out_of_range.403 if a key is missing in the JSON object.
  2. Will use value during deserialization and fall back to the default value for the respective type of the member variable if a key in the JSON object is missing. The generated from_json() function default constructs an object and uses its values as the defaults when calling the value function.

Parameters

type (in)
name of the type (class, struct) to serialize/deserialize
member (in)
name of the (public) member variable to serialize/deserialize; up to 64 members can be given as comma-separated list

Default definition

The macros add two functions to the namespace which take care of the serialization and deserialization:

void to_json(nlohmann::json&, const type&);
void from_json(const nlohmann::json&, type&);

See examples below for the concrete generated code.

Notes

Prerequisites

  1. The type type must be default constructible. See How can I use get() for non-default constructible/non-copyable types? for how to overcome this limitation.
  2. The macro must be used outside the type (class/struct).
  3. The passed members must be public.

Implementation limits

  • The current implementation is limited to at most 64 member variables. If you want to serialize/deserialize types with more than 64 member variables, you need to define the to_json/from_json functions manually.
  • The macros only work for the nlohmann::json type; other specializations such as nlohmann::ordered_json are currently unsupported.

Examples

Example (1): NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE

Consider the following complete example:

#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

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 (json::exception& e)
    {
        std::cout << "deserialization failed: " << e.what() << std::endl;
    }
}

Output:

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

Notes:

  • ns::person is default-constructible. This is a requirement for using the macro.
  • ns::person has only public member variables. This makes NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE applicable.
  • The macro NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE is used outside the class, but inside its namespace ns.
  • A missing key "age" in the deserialization yields an exception. To fall back to the default value, NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT can be used.

The macro is equivalent to:

#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

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

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");
}
} // 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 (json::exception& e)
    {
        std::cout << "deserialization failed: " << e.what() << std::endl;
    }
}
Example (2): NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT

Consider the following complete example:

#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

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

Output:

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

Notes:

  • ns::person is default-constructible. This is a requirement for using the macro.
  • ns::person has only public member variables. This makes NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT applicable.
  • The macro NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT is used outside the class, but inside its namespace ns.
  • A missing key "age" in the deserialization does not yield an exception. Instead, the default value -1 is used.

The macro is equivalent to:

#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

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_)
    {}
};

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

Note how a default-initialized person object is used in the from_json to fill missing values.

See also

Version history

  1. Added in version 3.9.0.
  2. Added in version 3.11.0.

Last update: May 1, 2022
Back to top