-
-
Notifications
You must be signed in to change notification settings - Fork 7.2k
New macros for the named JSON convertor generation #4563
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
base: develop
Are you sure you want to change the base?
Changes from 5 commits
32383af
4f1f4d5
3baca54
cb4c1d5
27b9408
96fe8dc
48676b1
070ca3f
f00f894
cd0ed45
aa3a920
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 |
|---|---|---|
|
|
@@ -817,14 +817,33 @@ Some important things: | |
|
|
||
| #### Simplify your life with macros | ||
|
|
||
|
|
||
| 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 several macros to make your life easier if you want to use a JSON object as serialization: | ||
|
|
||
| - `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(name, member1, member2, ...)` | ||
| - `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_NAMES(name, "json_member1", member1, "json_member2", member2, ...)` | ||
| - `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(name, member1, member2, ...)` | ||
| - `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(name, "json_member1", member1, "json_member2", member2, ...)` | ||
| - `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(name, member1, member2, ...)` | ||
| - `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(name, "json_member1", member1, "json_member2", member2, ...)` | ||
| - `NLOHMANN_DEFINE_TYPE_INTRUSIVE(name, member1, member2, ...)` | ||
| - `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_NAMES(name, "json_member1", member1, "json_member2", member2, ...)` | ||
| - `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(name, member1, member2, ...)` | ||
| - `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(name, "json_member1", member1, "json_member2", member2, ...)` | ||
| - `NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(name, member1, member2, ...)` | ||
| - `NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(name, "json_member1", member1, "json_member2", member2, ...)` | ||
|
|
||
| The explanation for the names is such: | ||
|
|
||
| - `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. | ||
| - The `NON_INTRUSIVE` macros should be defined inside the namespace of the class/struct to create code for and thus doesn't have access to the private fields, the `INTRUSIVE` is to be defined inside the class/struct. | ||
| - The `WITH_DEFAULT` macros should be used when not all fields are required to be present in the JSON, the ones without `WITH_DEFAULTS` will raise an exception if the fields are missing. | ||
| - The `ONLY_SERIALIZE` macros should be used if you only want the `to_json` function to be created. This option excludes the `WITH_DEFAULT` variant since it is only applicable to `from_json`. | ||
| - The `WITH_NAMES` macros should be used if you want custom names for you JSON fields, the ones without `WITH_NAMES` will use the member names for JSON fields. | ||
| - Each of the aforementioned macros has a `DEFINE_DERIVED_TYPE` variant (e.g. `NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE`) which can be used for the child class, with parent that already have `to_json`/`from_json` defined, possibly with a macro of its own. | ||
|
|
||
| In both macros, the first parameter is the name of the class/struct, and all remaining parameters name the members. | ||
| For all the macros, the first parameter is the name of the class/struct, and all remaining parameters name the members. The `DERIVED` variants require additional second argument for the parent class. | ||
|
||
|
|
||
| ##### Examples | ||
|
|
||
|
|
@@ -836,7 +855,19 @@ namespace ns { | |
| } | ||
| ``` | ||
|
|
||
| Here is an example with private members, where `NLOHMANN_DEFINE_TYPE_INTRUSIVE` is needed: | ||
| If you want to inherit the `person` struct and add a field to it, it can be done with: | ||
|
|
||
| ```cpp | ||
| namespace ns { | ||
| struct person_derived : person { | ||
| std:string email; | ||
| }; | ||
|
|
||
| NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(person_derived, person, email) | ||
| } | ||
| ``` | ||
|
|
||
| Here is an example with private members, where `INTRUSIVE` is needed: | ||
|
|
||
| ```cpp | ||
| namespace ns { | ||
|
|
@@ -852,6 +883,24 @@ namespace ns { | |
| } | ||
| ``` | ||
|
|
||
| Or in case if you use some naming convention that you do not want to expose to JSON: | ||
|
|
||
| ```cpp | ||
| namespace ns { | ||
| class address { | ||
| private: | ||
| std::string m_street; | ||
| int m_housenumber; | ||
| int m_postcode; | ||
|
|
||
| public: | ||
| NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_NAMES(address, "street", m_street, | ||
| "housenumber", m_housenumber, | ||
| "postcode", m_postcode) | ||
| }; | ||
| } | ||
| ``` | ||
|
|
||
| #### How do I convert third-party types? | ||
|
|
||
| This requires a bit more advanced technique. But first, let's see how this conversion mechanism works: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| #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; | ||
| }; | ||
|
|
||
| void to_json(nlohmann::json& nlohmann_json_j, const person& nlohmann_json_t) | ||
| { | ||
| nlohmann_json_j["json_name"] = nlohmann_json_t.name; | ||
| nlohmann_json_j["json_address"] = nlohmann_json_t.address; | ||
| nlohmann_json_j["json_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("json_name"); | ||
| nlohmann_json_t.address = nlohmann_json_j.at("json_address"); | ||
| nlohmann_json_t.age = nlohmann_json_j.at("json_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"({"json_address": "742 Evergreen Terrace", "json_age": 40, "json_name": "Homer Simpson"})"_json; | ||
| auto p2 = j2.template get<ns::person>(); | ||
|
|
||
| // incomplete deserialization: | ||
| json j3 = R"({"json_address": "742 Evergreen Terrace", "json_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; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| serialization: {"json_address":"744 Evergreen Terrace","json_age":60,"json_name":"Ned Flanders"} | ||
| deserialization failed: [json.exception.out_of_range.403] key 'json_age' not found |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| #include <iostream> | ||
| #include <nlohmann/json.hpp> | ||
|
|
||
| using json = nlohmann::json; | ||
| using namespace nlohmann::literals; | ||
|
|
||
| namespace ns | ||
| { | ||
| struct person | ||
| { | ||
| std::string name; | ||
| std::string address; | ||
|
Check notice on line 12 in docs/examples/nlohmann_define_type_non_intrusive_with_names_macro.cpp
|
||
| int age; | ||
| }; | ||
|
|
||
| NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_NAMES(person, "json_name", name, "json_address", address, "json_age", 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"({"json_address": "742 Evergreen Terrace", "json_age": 40, "json_name": "Homer Simpson"})"_json; | ||
| auto p2 = j2.template get<ns::person>(); | ||
|
|
||
| // incomplete deserialization: | ||
| json j3 = R"({"json_address": "742 Evergreen Terrace", "json_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; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| serialization: {"json_address":"744 Evergreen Terrace","json_age":60,"json_name":"Ned Flanders"} | ||
| deserialization failed: [json.exception.out_of_range.403] key 'json_age' not found |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| # The Named Conversion Macros | ||
|
||
|
|
||
| ```cpp | ||
| #define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_NAMES(type, "json_member_name", member...) | ||
| #define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(type, "json_member_name", member...) | ||
| #define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(type, "json_member_name", member...) | ||
| #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_NAMES(type, "json_member_name", member...) | ||
| #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(type, "json_member_name", member...) | ||
| #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(type, "json_member_name", member...) | ||
| #define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_NAMES(type, base_type, "json_member_name", member...) | ||
| #define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(type, base_type, "json_member_name", member...) | ||
| #define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(type, base_type, "json_member_name", member...) | ||
| #define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_NAMES(type, base_type, "json_member_name", member...) | ||
| #define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(type, base_type, "json_member_name", member...) | ||
| #define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(type, base_type, "json_member_name", member...) | ||
| ``` | ||
|
|
||
| These macros can be used in case you want to use the custom names for the member variables in the resulting JSON. | ||
| They behave exactly as their non-`WITH_NAMES` counterparts, but require an additional parameter for each member variable | ||
| which will be used in JSON. Both serialization and deserialization will only use the custom names for JSON, the names of | ||
| the member variables themselves will be ignored. | ||
|
|
||
| Using the named conversion macros will halve the maximum number of member variables from 63 to 31. | ||
|
|
||
| For further information please refer to the corresponding macros without `WITH_NAMES`. | ||
|
|
||
| ## Parameters | ||
|
|
||
| `type` (in) | ||
| : name of the type (class, struct) to serialize/deserialize | ||
|
|
||
| `base_type` (in) | ||
| : name of the base type (class, struct) `type` is derived from (used only in `DEFINE_DERIVED_TYPE` macros) | ||
|
|
||
| `json_member_name` (in) | ||
| : used in named conversion macros, must be provided for each member variable and will be used as a member variable name in the resulting json | ||
|
|
||
| `member` (in) | ||
| : name of the member variable to serialize/deserialize | ||
|
|
||
| ## Examples | ||
|
|
||
| ??? example "Example (1): NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE" | ||
|
|
||
| Consider the following complete example: | ||
|
|
||
| ```cpp hl_lines="16" | ||
| --8<-- "examples/nlohmann_define_type_non_intrusive_with_names_macro.cpp" | ||
| ``` | ||
|
|
||
| Output: | ||
|
|
||
| ```json | ||
| --8<-- "examples/nlohmann_define_type_non_intrusive_with_names_macro.output" | ||
| ``` | ||
|
|
||
| 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_NAMES` applicable. | ||
| - The macro `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_NAMES` 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_WITH_NAMES` can be used. | ||
|
|
||
| The macro is equivalent to: | ||
|
|
||
| ```cpp hl_lines="16 17 18 19 20 21 22 23 24 25 26 27 28" | ||
| --8<-- "examples/nlohmann_define_type_non_intrusive_with_names_explicit.cpp" | ||
| ``` | ||
|
|
||
| ## Version history | ||
|
|
||
| 1. Added in version 3.11.x. | ||
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.
for your