Add macros NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT and ..._NON_INTRUSIVE_WITH_DEFAULT (#3143)
* Added new macros NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT. * Updated docs for NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT accordingly * Rephrased docs for NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT * Updated docs for NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT * Renamed default_obj in to avoid name clashes * Added test for serialization of default constructed object * Add const to getters for macro tests Co-authored-by: Chaoya Li <harry75369@gmail.com>
This commit is contained in:
parent
c11f98228d
commit
eec79d4e8a
@ -85,29 +85,31 @@ Some important things:
|
||||
|
||||
If you just want to serialize/deserialize some structs, the `to_json`/`from_json` functions can be a lot of boilerplate.
|
||||
|
||||
There are two macros to make your life easier as long as you (1) want to use a JSON object as serialization and (2) want to use the member variable names as object keys in that object:
|
||||
There are four macros to make your life easier as long as you (1) want to use a JSON object as serialization and (2) want to use the member variable names as object keys in that object:
|
||||
|
||||
- `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(name, member1, member2, ...)` is to be defined inside the namespace of the class/struct to create code for.
|
||||
- `NLOHMANN_DEFINE_TYPE_INTRUSIVE(name, member1, member2, ...)` is to be defined inside the class/struct to create code for. This macro can also access private members.
|
||||
- `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(name, member1, member2, ...)` is to be defined inside the namespace of the class/struct to create code for. It will throw an exception in `from_json()` due to a missing value in the JSON object.
|
||||
- `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(name, member1, member2, ...)` is to be defined inside the namespace of the class/struct to create code for. It will not throw an exception in `from_json()` due to a missing value in the JSON object, but fills in values from object which is default-constructed by the type.
|
||||
- `NLOHMANN_DEFINE_TYPE_INTRUSIVE(name, member1, member2, ...)` is to be defined inside the class/struct to create code for. This macro can also access private members. It will throw an exception in `from_json()` due to a missing value in the JSON object.
|
||||
- `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(name, member1, member2, ...)` is to be defined inside the class/struct to create code for. This macro can also access private members. It will not throw an exception in `from_json()` due to a missing value in the JSON object, but fills in values from object which is default-constructed by the type.
|
||||
|
||||
In both macros, the first parameter is the name of the class/struct, and all remaining parameters name the members.
|
||||
In all macros, the first parameter is the name of the class/struct, and all remaining parameters name the members. You can read more docs about them starting from [here](macros.md#nlohmann_define_type_intrusivetype-member).
|
||||
|
||||
!!! note
|
||||
|
||||
At most 64 member variables can be passed to `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE` or `NLOHMANN_DEFINE_TYPE_INTRUSIVE`.
|
||||
At most 64 member variables can be passed to these macros.
|
||||
|
||||
??? example
|
||||
|
||||
The `to_json`/`from_json` functions for the `person` struct above can be created with:
|
||||
|
||||
|
||||
```cpp
|
||||
namespace ns {
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, address, age)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Here is an example with private members, where `NLOHMANN_DEFINE_TYPE_INTRUSIVE` is needed:
|
||||
|
||||
|
||||
```cpp
|
||||
namespace ns {
|
||||
class address {
|
||||
@ -115,7 +117,7 @@ In both macros, the first parameter is the name of the class/struct, and all rem
|
||||
std::string street;
|
||||
int housenumber;
|
||||
int postcode;
|
||||
|
||||
|
||||
public:
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(address, street, housenumber, postcode)
|
||||
};
|
||||
|
@ -186,12 +186,12 @@ When defined to `0`, implicit conversions are switched off. By default, implicit
|
||||
??? example
|
||||
|
||||
This is an example for an implicit conversion:
|
||||
|
||||
|
||||
```cpp
|
||||
json j = "Hello, world!";
|
||||
std::string s = j;
|
||||
```
|
||||
|
||||
|
||||
When `JSON_USE_IMPLICIT_CONVERSIONS` is defined to `0`, the code above does no longer compile. Instead, it must be
|
||||
written like this:
|
||||
|
||||
@ -220,6 +220,10 @@ The first parameter is the name of the class/struct, and all remaining parameter
|
||||
|
||||
See [Simplify your life with macros](arbitrary_types.md#simplify-your-life-with-macros) for an example.
|
||||
|
||||
## `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(type, member...)`
|
||||
|
||||
This macro is similar to `NLOHMANN_DEFINE_TYPE_INTRUSIVE`. It will not throw an exception in `from_json()` due to a missing value in the JSON object, but can throw due to a mismatched type. In order to support that it requires that the type be default constructible. The `from_json()` function default constructs an object and uses its values as the defaults when calling the `value()` function.
|
||||
|
||||
## `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(type, member...)`
|
||||
|
||||
This macro can be used to simplify the serialization/deserialization of types if (1) want to use a JSON object as
|
||||
@ -231,6 +235,10 @@ first parameter is the name of the class/struct, and all remaining parameters na
|
||||
|
||||
See [Simplify your life with macros](arbitrary_types.md#simplify-your-life-with-macros) for an example.
|
||||
|
||||
## `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(type, member...)`
|
||||
|
||||
This macro is similar to `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`. It will not throw an exception in `from_json()` due to a missing value in the JSON object, but can throw due to a mismatched type. In order to support that it requires that the type be default constructible. The `from_json()` function default constructs an object and uses its values as the defaults when calling the `value()` function.
|
||||
|
||||
## `NLOHMANN_JSON_SERIALIZE_ENUM(type, ...)`
|
||||
|
||||
This macro simplifies the serialization/deserialization of enum types. See
|
||||
|
@ -334,6 +334,7 @@
|
||||
|
||||
#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1;
|
||||
#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1);
|
||||
#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1);
|
||||
|
||||
/*!
|
||||
@brief macro
|
||||
@ -344,6 +345,10 @@
|
||||
friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
|
||||
friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
|
||||
|
||||
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \
|
||||
friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
|
||||
friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
|
||||
|
||||
/*!
|
||||
@brief macro
|
||||
@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
|
||||
@ -353,6 +358,10 @@
|
||||
inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
|
||||
inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
|
||||
|
||||
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \
|
||||
inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
|
||||
inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
|
||||
|
||||
|
||||
// inspired from https://stackoverflow.com/a/26745591
|
||||
// allows to call any std function as if (e.g. with begin):
|
||||
|
@ -2631,6 +2631,7 @@ using is_detected_convertible =
|
||||
|
||||
#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1;
|
||||
#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1);
|
||||
#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1);
|
||||
|
||||
/*!
|
||||
@brief macro
|
||||
@ -2641,6 +2642,10 @@ using is_detected_convertible =
|
||||
friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
|
||||
friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
|
||||
|
||||
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \
|
||||
friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
|
||||
friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
|
||||
|
||||
/*!
|
||||
@brief macro
|
||||
@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
|
||||
@ -2650,6 +2655,10 @@ using is_detected_convertible =
|
||||
inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
|
||||
inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
|
||||
|
||||
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \
|
||||
inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
|
||||
inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
|
||||
|
||||
|
||||
// inspired from https://stackoverflow.com/a/26745591
|
||||
// allows to call any std function as if (e.g. with begin):
|
||||
|
@ -59,6 +59,42 @@ class person_with_private_data
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_with_private_data, age, name, metadata)
|
||||
};
|
||||
|
||||
class person_with_private_data_2
|
||||
{
|
||||
private:
|
||||
std::string name{};
|
||||
int age = 0;
|
||||
json metadata = nullptr;
|
||||
|
||||
public:
|
||||
bool operator==(const person_with_private_data_2& rhs) const
|
||||
{
|
||||
return name == rhs.name && age == rhs.age && metadata == rhs.metadata;
|
||||
}
|
||||
|
||||
person_with_private_data_2() = default;
|
||||
person_with_private_data_2(std::string name_, int age_, json metadata_)
|
||||
: name(std::move(name_))
|
||||
, age(age_)
|
||||
, metadata(std::move(metadata_))
|
||||
{}
|
||||
|
||||
std::string getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
int getAge() const
|
||||
{
|
||||
return age;
|
||||
}
|
||||
json getMetadata() const
|
||||
{
|
||||
return metadata;
|
||||
}
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(person_with_private_data_2, age, name, metadata)
|
||||
};
|
||||
|
||||
class person_without_private_data_1
|
||||
{
|
||||
public:
|
||||
@ -103,6 +139,41 @@ class person_without_private_data_2
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_without_private_data_2, age, name, metadata)
|
||||
|
||||
class person_without_private_data_3
|
||||
{
|
||||
public:
|
||||
std::string name{};
|
||||
int age = 0;
|
||||
json metadata = nullptr;
|
||||
|
||||
bool operator==(const person_without_private_data_3& rhs) const
|
||||
{
|
||||
return name == rhs.name && age == rhs.age && metadata == rhs.metadata;
|
||||
}
|
||||
|
||||
person_without_private_data_3() = default;
|
||||
person_without_private_data_3(std::string name_, int age_, json metadata_)
|
||||
: name(std::move(name_))
|
||||
, age(age_)
|
||||
, metadata(std::move(metadata_))
|
||||
{}
|
||||
|
||||
std::string getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
int getAge() const
|
||||
{
|
||||
return age;
|
||||
}
|
||||
json getMetadata() const
|
||||
{
|
||||
return metadata;
|
||||
}
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(person_without_private_data_3, age, name, metadata)
|
||||
|
||||
class person_with_private_alphabet
|
||||
{
|
||||
public:
|
||||
@ -231,7 +302,7 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_with_public_alphabet, a, b, c, d, e, f
|
||||
|
||||
} // namespace persons
|
||||
|
||||
TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE", T,
|
||||
TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE", T,
|
||||
persons::person_with_private_data,
|
||||
persons::person_without_private_data_1,
|
||||
persons::person_without_private_data_2)
|
||||
@ -257,6 +328,40 @@ TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRU
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT", T,
|
||||
persons::person_with_private_data_2,
|
||||
persons::person_without_private_data_3)
|
||||
{
|
||||
SECTION("person with default values")
|
||||
{
|
||||
// serialization of default constructed object
|
||||
T p0;
|
||||
CHECK(json(p0).dump() == "{\"age\":0,\"metadata\":null,\"name\":\"\"}");
|
||||
|
||||
// serialization
|
||||
T p1("Erik", 1, {{"haircuts", 2}});
|
||||
CHECK(json(p1).dump() == "{\"age\":1,\"metadata\":{\"haircuts\":2},\"name\":\"Erik\"}");
|
||||
|
||||
// deserialization
|
||||
auto p2 = json(p1).get<T>();
|
||||
CHECK(p2 == p1);
|
||||
|
||||
// roundtrip
|
||||
CHECK(T(json(p1)) == p1);
|
||||
CHECK(json(T(json(p1))) == json(p1));
|
||||
|
||||
// check default value in case of missing field
|
||||
json j = json(p1);
|
||||
j.erase("name");
|
||||
j.erase("age");
|
||||
j.erase("metadata");
|
||||
T p3 = j.get<T>();
|
||||
CHECK(p3.getName() == "");
|
||||
CHECK(p3.getAge() == 0);
|
||||
CHECK(p3.getMetadata() == nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_TEMPLATE("Serialization/deserialization of classes with 26 public/private member variables via NLOHMANN_DEFINE_TYPE_INTRUSIVE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE", T,
|
||||
persons::person_with_private_alphabet,
|
||||
persons::person_with_public_alphabet)
|
||||
|
Loading…
Reference in New Issue
Block a user