diff --git a/README.md b/README.md index 12ce676ab..8fbab464c 100644 --- a/README.md +++ b/README.md @@ -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, ...)` 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, ...)` +- `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, ...)` -In both macros, the first parameter is the name of the class/struct, and all remaining parameters name the members. +The explanation for the names is such: + +- 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. + +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: diff --git a/docs/examples/nlohmann_define_type_non_intrusive_with_names_explicit.cpp b/docs/examples/nlohmann_define_type_non_intrusive_with_names_explicit.cpp new file mode 100644 index 000000000..e054f4c13 --- /dev/null +++ b/docs/examples/nlohmann_define_type_non_intrusive_with_names_explicit.cpp @@ -0,0 +1,53 @@ +#include +#include + +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(); + + // incomplete deserialization: + json j3 = R"({"json_address": "742 Evergreen Terrace", "json_name": "Maggie Simpson"})"_json; + try + { + auto p3 = j3.template get(); + } + catch (const json::exception& e) + { + std::cout << "deserialization failed: " << e.what() << std::endl; + } +} diff --git a/docs/examples/nlohmann_define_type_non_intrusive_with_names_explicit.output b/docs/examples/nlohmann_define_type_non_intrusive_with_names_explicit.output new file mode 100644 index 000000000..c4a689b3f --- /dev/null +++ b/docs/examples/nlohmann_define_type_non_intrusive_with_names_explicit.output @@ -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 diff --git a/docs/examples/nlohmann_define_type_non_intrusive_with_names_macro.cpp b/docs/examples/nlohmann_define_type_non_intrusive_with_names_macro.cpp new file mode 100644 index 000000000..36114e724 --- /dev/null +++ b/docs/examples/nlohmann_define_type_non_intrusive_with_names_macro.cpp @@ -0,0 +1,41 @@ +#include +#include + +using json = nlohmann::json; +using namespace nlohmann::literals; + +namespace ns +{ +struct person +{ + std::string name; + std::string address; + 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(); + + // incomplete deserialization: + json j3 = R"({"json_address": "742 Evergreen Terrace", "json_name": "Maggie Simpson"})"_json; + try + { + auto p3 = j3.template get(); + } + catch (const json::exception& e) + { + std::cout << "deserialization failed: " << e.what() << std::endl; + } +} diff --git a/docs/examples/nlohmann_define_type_non_intrusive_with_names_macro.output b/docs/examples/nlohmann_define_type_non_intrusive_with_names_macro.output new file mode 100644 index 000000000..37f4eb414 --- /dev/null +++ b/docs/examples/nlohmann_define_type_non_intrusive_with_names_macro.output @@ -0,0 +1,2 @@ +serialization: {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"} +deserialization failed: [json.exception.out_of_range.403] key 'age' not found diff --git a/docs/mkdocs/docs/api/macros/nlohmann_define_type_with_names.md b/docs/mkdocs/docs/api/macros/nlohmann_define_type_with_names.md new file mode 100644 index 000000000..cc1ef2701 --- /dev/null +++ b/docs/mkdocs/docs/api/macros/nlohmann_define_type_with_names.md @@ -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. \ No newline at end of file diff --git a/docs/mkdocs/mkdocs.yml b/docs/mkdocs/mkdocs.yml index ccf75a6f8..308872f9d 100644 --- a/docs/mkdocs/mkdocs.yml +++ b/docs/mkdocs/mkdocs.yml @@ -288,13 +288,27 @@ nav: - 'NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE': api/macros/nlohmann_define_derived_type.md - 'NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT': api/macros/nlohmann_define_derived_type.md - 'NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_derived_type.md + - 'NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_NAMES': api/macros/nlohmann_define_type_with_names.md + - 'NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES': api/macros/nlohmann_define_type_with_names.md + - 'NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES': api/macros/nlohmann_define_type_with_names.md - 'NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE': api/macros/nlohmann_define_derived_type.md - 'NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT': api/macros/nlohmann_define_derived_type.md - 'NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_derived_type.md + - 'NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_NAMES': api/macros/nlohmann_define_type_with_names.md + - 'NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES': api/macros/nlohmann_define_type_with_names.md + - 'NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES': api/macros/nlohmann_define_type_with_names.md - 'NLOHMANN_DEFINE_TYPE_INTRUSIVE': api/macros/nlohmann_define_type_intrusive.md - 'NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT': api/macros/nlohmann_define_type_intrusive.md + - 'NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_type_intrusive.md + - 'NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_NAMES': api/macros/nlohmann_define_type_with_names.md + - 'NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES': api/macros/nlohmann_define_type_with_names.md + - 'NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES': api/macros/nlohmann_define_type_with_names.md - 'NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE': api/macros/nlohmann_define_type_non_intrusive.md - 'NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT': api/macros/nlohmann_define_type_non_intrusive.md + - 'NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_type_non_intrusive.md + - 'NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_NAMES': api/macros/nlohmann_define_type_with_names.md + - 'NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES': api/macros/nlohmann_define_type_with_names.md + - 'NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES': api/macros/nlohmann_define_type_with_names.md - 'NLOHMANN_JSON_NAMESPACE': api/macros/nlohmann_json_namespace.md - 'NLOHMANN_JSON_NAMESPACE_BEGIN': api/macros/nlohmann_json_namespace_begin.md - 'NLOHMANN_JSON_NAMESPACE_END': api/macros/nlohmann_json_namespace_begin.md