NLOHMANN_DEFINE_TYPE_INTRUSIVE, NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE¶
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(type, member...) // (1)
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(type, member...) // (2)
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(type, member...) // (3)
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 inside the class/struct to create code for. Unlike NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
, it can access private members. The first parameter is the name of the class/struct, and all remaining parameters name the members.
- Will use
at
during deserialization and will throwout_of_range.403
if a key is missing in the JSON object. - 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 generatedfrom_json()
function default constructs an object and uses its values as the defaults when calling thevalue
function. - Only defines the serialization. Useful in cases when the type does not have a default constructor and only serialization in required.
Parameters¶
type
(in)- name of the type (class, struct) to serialize/deserialize
member
(in)- name of the member variable to serialize/deserialize; up to 64 members can be given as comma-separated list
Default definition¶
The macros add two friend functions to the class which take care of the serialization and deserialization:
friend void to_json(nlohmann::json&, const type&);
friend void from_json(const nlohmann::json&, type&); // except (3)
See examples below for the concrete generated code.
Notes¶
Prerequisites
- The type
type
must be default constructible (except (3)). See How can I useget()
for non-default constructible/non-copyable types? for how to overcome this limitation. - The macro must be used inside the type (class/struct).
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 asnlohmann::ordered_json
are currently unsupported.
Examples¶
Example (1): NLOHMANN_DEFINE_TYPE_INTRUSIVE
Consider the following complete example:
#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.template get<ns::person>();
// incomplete deserialization:
json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json;
try
{
auto p3 = j3.template get<ns::person>();
}
catch (const 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 private member variables. This makesNLOHMANN_DEFINE_TYPE_INTRUSIVE
applicable, but notNLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
.- The macro
NLOHMANN_DEFINE_TYPE_INTRUSIVE
is used inside the class. - A missing key "age" in the deserialization yields an exception. To fall back to the default value,
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT
can be used.
The macro is equivalent to:
#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_)
{}
friend 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;
}
friend 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.template get<ns::person>();
// incomplete deserialization:
json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json;
try
{
auto p3 = j3.template get<ns::person>();
}
catch (const json::exception& e)
{
std::cout << "deserialization failed: " << e.what() << std::endl;
}
}
Example (2): NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT
Consider the following complete example:
#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.template get<ns::person>();
// incomplete deserialization:
json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json;
auto p3 = j3.template 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 private member variables. This makesNLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT
applicable, but notNLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT
.- The macro
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT
is used inside the class. - 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;
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_)
{}
friend 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;
}
friend 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.template get<ns::person>();
// incomplete deserialization:
json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json;
auto p3 = j3.template 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.
Example (3): NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE
Consider the following complete example:
#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(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;
}
Output:
serialization: {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"}
Notes:
ns::person
is non-default-constructible. This allows this macro to be used instead ofNLOHMANN_DEFINE_TYPE_INTRUSIVE
andNLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT
.ns::person
has private member variables. This makesNLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE
applicable, but notNLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE
.- The macro
NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE
is used inside the class.
The macro is equivalent to:
#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_)
{}
friend 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;
}
};
} // 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;
}
See also¶
- NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE{_WITH_DEFAULT, _ONLY_SERIALIZE} for a similar macro that can be defined outside the type.
- Arbitrary Type Conversions for an overview.
Version history¶
- Added in version 3.9.0.
- Added in version 3.11.0.
- Added in version TODO.