-
-
Notifications
You must be signed in to change notification settings - Fork 6.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow default values for NLOHMANN_DEFINE_TYPE_INTRUSIVE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE #2819
Allow default values for NLOHMANN_DEFINE_TYPE_INTRUSIVE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE #2819
Changes from 4 commits
76f6740
7aeda9d
5a5832e
5664233
d8ba9f4
35c37c8
f452a92
98ac25b
db0287d
a5104d1
7ea2732
44201db
69de3eb
6c99b75
378fbda
5a49932
eeb48dd
b475990
1d7639a
3363fc8
a4c9fa0
23803e2
714d9f5
a5fd035
44a7dce
45c77b1
8b643e4
97eb641
e77ae77
5bbbe5f
16758b9
88ea375
3675770
519dcc5
94e87ff
2d994d9
2668c89
d65ee13
9538454
8e077ae
1f1b6a7
fc0a138
3ff2517
fdb0f07
740c896
7e63060
6a81073
2e82056
9661e66
8da9e25
a281280
d9562c1
e9797ac
974e386
b83acfe
0c4af65
9e7550a
8c6ea74
5b6fd51
321cc51
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -85,37 +85,39 @@ 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 of the namespace of the class/struct to create code for. | ||||||
- `NLOHMANN_DEFINE_TYPE_INTRUSIVE(name, member1, member2, ...)` is to be defined inside of the class/struct to create code for. This macro can also access private members. | ||||||
- `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(name, member1, member2, ...)` is to be defined inside of the namespace of the class/struct to create code for. | ||||||
- `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(name, member1, member2, ...)` is to be defined inside of the class/struct to create code for. This macro can also access private members. | ||||||
|
||||||
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](https://github.com/nlohmann/json/blob/develop/doc/mkdocs/docs/features/macros.md#nlohmann_define_type_intrusivetype-member). | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
!!! 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 { | ||||||
private: | ||||||
std::string street; | ||||||
int housenumber; | ||||||
int postcode; | ||||||
|
||||||
public: | ||||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(address, street, housenumber, postcode) | ||||||
}; | ||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -27,7 +27,7 @@ The library targets C++11, but also supports some features introduced in later C | |||||
## `JSON_NOEXCEPTION` | ||||||
|
||||||
Exceptions can be switched off by defining the symbol `JSON_NOEXCEPTION`. | ||||||
When defining `JSON_NOEXCEPTION`, `#!cpp try` is replaced by `#!cpp if (true)`, | ||||||
When defining `JSON_NOEXCEPTION`, `#!cpp try` is replaced by `#!cpp if (true)`, | ||||||
`#!cpp catch` is replaced by `#!cpp if (false)`, and `#!cpp throw` is replaced by `#!cpp std::abort()`. | ||||||
|
||||||
The same effect is achieved by setting the compiler flag `-fno-exceptions`. | ||||||
|
@@ -55,12 +55,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: | ||||||
|
||||||
```cpp | ||||||
|
@@ -79,6 +79,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, 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. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
## `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 serialization and (2) want to use the member variable names as object keys in that object. | ||||||
|
@@ -88,6 +92,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_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, 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. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
## `NLOHMANN_JSON_SERIALIZE_ENUM(type, ...)` | ||||||
|
||||||
This macro simplifies the serialization/deserialization of enum types. See [Specializing enum conversion](enum_conversion.md) for more information. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -272,6 +272,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, default_obj.v1); | ||
|
||
/*! | ||
@brief macro | ||
|
@@ -282,6 +283,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 default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } | ||
|
||
/*! | ||
@brief macro | ||
@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE | ||
|
@@ -291,6 +296,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 default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking that having There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Making it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nothing in general, just could be specific use cases where having a long-lived one of these would be a problem for people. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thinking about this, I also think having a long-lived object to be surprising. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's not necessarily true. We ran into situations recently where There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be safer to add |
||
|
||
#ifndef JSON_USE_IMPLICIT_CONVERSIONS | ||
#define JSON_USE_IMPLICIT_CONVERSIONS 1 | ||
#endif | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you actually test how JSON objects with missing values are serialized? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Test added. |
||
{ | ||
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() | ||
{ | ||
return name; | ||
} | ||
int getAge() | ||
{ | ||
return age; | ||
} | ||
json getMetadata() | ||
{ | ||
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() | ||
{ | ||
return name; | ||
} | ||
int getAge() | ||
{ | ||
return age; | ||
} | ||
json getMetadata() | ||
{ | ||
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,36 @@ 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 | ||
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) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be good to add a comment about the difference to the previous macros.
Furthermore, the order should be.