From 360ce457f46f03111332f473fdbb3a353f16723c Mon Sep 17 00:00:00 2001 From: Vyacheslav Zhdanovskiy Date: Sun, 26 Nov 2023 15:18:20 +0300 Subject: [PATCH] Add serialization-only user defined type macros (#3816) --- ...type_intrusive_only_serialize_explicit.cpp | 38 ++++++++++++ ...e_intrusive_only_serialize_explicit.output | 1 + ...ne_type_intrusive_only_serialize_macro.cpp | 33 ++++++++++ ...type_intrusive_only_serialize_macro.output | 1 + ..._non_intrusive_only_serialize_explicit.cpp | 31 ++++++++++ ...n_intrusive_only_serialize_explicit.output | 1 + ...ype_non_intrusive_only_serialize_macro.cpp | 26 ++++++++ ..._non_intrusive_only_serialize_macro.output | 1 + docs/mkdocs/docs/api/macros/index.md | 6 +- .../macros/nlohmann_define_type_intrusive.md | 40 +++++++++++-- .../nlohmann_define_type_non_intrusive.md | 42 +++++++++++-- docs/mkdocs/docs/features/macros.md | 14 +++++ include/nlohmann/detail/macro_scope.hpp | 6 ++ single_include/nlohmann/json.hpp | 6 ++ tests/src/unit-udt_macro.cpp | 60 +++++++++++++++++++ 15 files changed, 293 insertions(+), 13 deletions(-) create mode 100644 docs/examples/nlohmann_define_type_intrusive_only_serialize_explicit.cpp create mode 100644 docs/examples/nlohmann_define_type_intrusive_only_serialize_explicit.output create mode 100644 docs/examples/nlohmann_define_type_intrusive_only_serialize_macro.cpp create mode 100644 docs/examples/nlohmann_define_type_intrusive_only_serialize_macro.output create mode 100644 docs/examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.cpp create mode 100644 docs/examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.output create mode 100644 docs/examples/nlohmann_define_type_non_intrusive_only_serialize_macro.cpp create mode 100644 docs/examples/nlohmann_define_type_non_intrusive_only_serialize_macro.output diff --git a/docs/examples/nlohmann_define_type_intrusive_only_serialize_explicit.cpp b/docs/examples/nlohmann_define_type_intrusive_only_serialize_explicit.cpp new file mode 100644 index 000000000..ea422beb5 --- /dev/null +++ b/docs/examples/nlohmann_define_type_intrusive_only_serialize_explicit.cpp @@ -0,0 +1,38 @@ +#include +#include + +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; +} diff --git a/docs/examples/nlohmann_define_type_intrusive_only_serialize_explicit.output b/docs/examples/nlohmann_define_type_intrusive_only_serialize_explicit.output new file mode 100644 index 000000000..12f503307 --- /dev/null +++ b/docs/examples/nlohmann_define_type_intrusive_only_serialize_explicit.output @@ -0,0 +1 @@ +serialization: {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"} diff --git a/docs/examples/nlohmann_define_type_intrusive_only_serialize_macro.cpp b/docs/examples/nlohmann_define_type_intrusive_only_serialize_macro.cpp new file mode 100644 index 000000000..8213f9063 --- /dev/null +++ b/docs/examples/nlohmann_define_type_intrusive_only_serialize_macro.cpp @@ -0,0 +1,33 @@ +#include +#include + +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; +} diff --git a/docs/examples/nlohmann_define_type_intrusive_only_serialize_macro.output b/docs/examples/nlohmann_define_type_intrusive_only_serialize_macro.output new file mode 100644 index 000000000..12f503307 --- /dev/null +++ b/docs/examples/nlohmann_define_type_intrusive_only_serialize_macro.output @@ -0,0 +1 @@ +serialization: {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"} diff --git a/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.cpp b/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.cpp new file mode 100644 index 000000000..8890e03af --- /dev/null +++ b/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.cpp @@ -0,0 +1,31 @@ +#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["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; +} diff --git a/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.output b/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.output new file mode 100644 index 000000000..12f503307 --- /dev/null +++ b/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.output @@ -0,0 +1 @@ +serialization: {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"} diff --git a/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_macro.cpp b/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_macro.cpp new file mode 100644 index 000000000..7aef23af6 --- /dev/null +++ b/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_macro.cpp @@ -0,0 +1,26 @@ +#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_ONLY_SERIALIZE(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; +} diff --git a/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_macro.output b/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_macro.output new file mode 100644 index 000000000..12f503307 --- /dev/null +++ b/docs/examples/nlohmann_define_type_non_intrusive_only_serialize_macro.output @@ -0,0 +1 @@ +serialization: {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"} diff --git a/docs/mkdocs/docs/api/macros/index.md b/docs/mkdocs/docs/api/macros/index.md index 099dfa671..ae9eb2044 100644 --- a/docs/mkdocs/docs/api/macros/index.md +++ b/docs/mkdocs/docs/api/macros/index.md @@ -50,9 +50,11 @@ header. See also the [macro overview page](../../features/macros.md). ## Serialization/deserialization macros -- [**NLOHMANN_DEFINE_TYPE_INTRUSIVE(type, member...)**
**NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(type, member...)**][DefInt] +- [**NLOHMANN_DEFINE_TYPE_INTRUSIVE(type, member...)**
**NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(type, member...)** +
**NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(type, member...)**][DefInt] \- serialization/deserialization of types _with_ access to private variables -- [**NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(type, member...)**
**NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(type, member...)**][DefNonInt] +- [**NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(type, member...)**
**NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(type, member...)** +
**NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(type, member...)**][DefNonInt] \- serialization/deserialization of types _without_ access to private variables - [**NLOHMANN_JSON_SERIALIZE_ENUM(type, ...)**](nlohmann_json_serialize_enum.md) - serialization/deserialization of enum types diff --git a/docs/mkdocs/docs/api/macros/nlohmann_define_type_intrusive.md b/docs/mkdocs/docs/api/macros/nlohmann_define_type_intrusive.md index 88df1d185..ad425810a 100644 --- a/docs/mkdocs/docs/api/macros/nlohmann_define_type_intrusive.md +++ b/docs/mkdocs/docs/api/macros/nlohmann_define_type_intrusive.md @@ -1,8 +1,9 @@ -# NLOHMANN_DEFINE_TYPE_INTRUSIVE, NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT +# NLOHMANN_DEFINE_TYPE_INTRUSIVE, NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE ```cpp #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 @@ -16,6 +17,7 @@ parameter is the name of the class/struct, and all remaining parameters name the 2. Will use [`value`](../basic_json/value.md) 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 generated `from_json()` function default constructs an object and uses its values as the defaults when calling the `value` function. +3. Only defines the serialization. Useful in cases when the type does not have a default constructor and only serialization in required. ## Parameters @@ -31,7 +33,7 @@ The macros add two friend functions to the class which take care of the serializ ```cpp friend void to_json(nlohmann::json&, const type&); -friend void from_json(const nlohmann::json&, type&); +friend void from_json(const nlohmann::json&, type&); // except (3) ``` See examples below for the concrete generated code. @@ -40,7 +42,7 @@ See examples below for the concrete generated code. !!! info "Prerequisites" - 1. The type `type` must be default constructible. See [How can I use `get()` for non-default + 1. The type `type` must be default constructible (except (3)). See [How can I use `get()` for non-default constructible/non-copyable types?][GetNonDefNonCopy] for how to overcome this limitation. 2. The macro must be used inside the type (class/struct). @@ -108,15 +110,42 @@ See examples below for the concrete generated code. The macro is equivalent to: - ```cpp hl_lines="21 22 23 24 25 26 27 28 29 30 31 32 33 34 35" + ```cpp hl_lines="22 23 24 25 26 27 28 29 30 31 32 33 34 35" --8<-- "examples/nlohmann_define_type_intrusive_with_default_explicit.cpp" ``` Note how a default-initialized `person` object is used in the `from_json` to fill missing values. +??? example "Example (3): NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE" + Consider the following complete example: + + ```cpp hl_lines="22" + --8<-- "examples/nlohmann_define_type_intrusive_only_serialize_macro.cpp" + ``` + + Output: + + ```json + --8<-- "examples/nlohmann_define_type_intrusive_only_serialize_macro.output" + ``` + + Notes: + + - `ns::person` is non-default-constructible. This allows this macro to be used instead of + `NLOHMANN_DEFINE_TYPE_INTRUSIVE` and `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT`. + - `ns::person` has private member variables. This makes `NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE` applicable, but not + `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE`. + - The macro `NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE` is used _inside_ the class. + + The macro is equivalent to: + + ```cpp hl_lines="22 22 23 24 25 26 27" + --8<-- "examples/nlohmann_define_type_intrusive_only_serialize_explicit.cpp" + ``` + ## See also -- [NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE / NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT](nlohmann_define_type_non_intrusive.md) +- [NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE{_WITH_DEFAULT, _ONLY_SERIALIZE}](nlohmann_define_type_non_intrusive.md) for a similar macro that can be defined _outside_ the type. - [Arbitrary Type Conversions](../../features/arbitrary_types.md) for an overview. @@ -124,3 +153,4 @@ See examples below for the concrete generated code. 1. Added in version 3.9.0. 2. Added in version 3.11.0. +3. Added in version TODO. diff --git a/docs/mkdocs/docs/api/macros/nlohmann_define_type_non_intrusive.md b/docs/mkdocs/docs/api/macros/nlohmann_define_type_non_intrusive.md index e1f1991b4..5830f8ca9 100644 --- a/docs/mkdocs/docs/api/macros/nlohmann_define_type_non_intrusive.md +++ b/docs/mkdocs/docs/api/macros/nlohmann_define_type_non_intrusive.md @@ -1,8 +1,9 @@ -# NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE, NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT +# NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE, NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE ```cpp #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(type, member...) // (1) #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(type, member...) // (2) +#define NLOHMANN_DEFINE_TYPE_NON_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 @@ -16,6 +17,7 @@ parameter is the name of the class/struct, and all remaining parameters name the 2. Will use [`value`](../basic_json/value.md) 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 generated `from_json()` function default constructs an object and uses its values as the defaults when calling the `value` function. +3. Only defines the serialization. Useful in cases when the type does not have a default constructor and only serialization in required. ## Parameters @@ -31,7 +33,7 @@ The macros add two functions to the namespace which take care of the serializati ```cpp void to_json(nlohmann::json&, const type&); -void from_json(const nlohmann::json&, type&); +void from_json(const nlohmann::json&, type&); // except (3) ``` See examples below for the concrete generated code. @@ -40,7 +42,7 @@ See examples below for the concrete generated code. !!! info "Prerequisites" - 1. The type `type` must be default constructible. See [How can I use `get()` for non-default constructible/non-copyable types?][GetNonDefNonCopy] + 1. The type `type` must be default constructible (except (3). See [How can I use `get()` for non-default constructible/non-copyable types?][GetNonDefNonCopy] for how to overcome this limitation. 2. The macro must be used outside the type (class/struct). 3. The passed members must be public. @@ -88,7 +90,7 @@ See examples below for the concrete generated code. Consider the following complete example: - ```cpp hl_lines="21" + ```cpp hl_lines="22" --8<-- "examples/nlohmann_define_type_non_intrusive_with_default_macro.cpp" ``` @@ -109,15 +111,42 @@ See examples below for the concrete generated code. The macro is equivalent to: - ```cpp hl_lines="21 22 23 24 25 26 27 28 29 30 31 32 33 34" + ```cpp hl_lines="22 23 24 25 26 27 28 29 30 31 32 33 34 35" --8<-- "examples/nlohmann_define_type_non_intrusive_with_default_explicit.cpp" ``` Note how a default-initialized `person` object is used in the `from_json` to fill missing values. +??? example "Example (3): NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE" + + Consider the following complete example: + + ```cpp hl_lines="16" + --8<-- "examples/nlohmann_define_type_non_intrusive_only_serialize_macro.cpp" + ``` + + Output: + + ```json + --8<-- "examples/nlohmann_define_type_non_intrusive_only_serialize_macro.output" + ``` + + Notes: + + - `ns::person` is non-default-constructible. This allows this macro to be used instead of + `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE` and `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT`. + - `ns::person` has only public member variables. This makes `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE` applicable. + - The macro `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE` is used _outside_ the class, but _inside_ its namespace `ns`. + + The macro is equivalent to: + + ```cpp hl_lines="16 17 18 19 20 21" + --8<-- "examples/nlohmann_define_type_non_intrusive_only_serialize_explicit.cpp" + ``` + ## See also -- [NLOHMANN_DEFINE_TYPE_INTRUSIVE / NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT](nlohmann_define_type_intrusive.md) +- [NLOHMANN_DEFINE_TYPE_INTRUSIVE{_WITH_DEFAULT, _ONLY_SERIALIZE}](nlohmann_define_type_intrusive.md) for a similar macro that can be defined _inside_ the type. - [Arbitrary Type Conversions](../../features/arbitrary_types.md) for an overview. @@ -125,3 +154,4 @@ See examples below for the concrete generated code. 1. Added in version 3.9.0. 2. Added in version 3.11.0. +3. Added in version TODO. diff --git a/docs/mkdocs/docs/features/macros.md b/docs/mkdocs/docs/features/macros.md index 1be95d35d..926741b09 100644 --- a/docs/mkdocs/docs/features/macros.md +++ b/docs/mkdocs/docs/features/macros.md @@ -119,6 +119,13 @@ an object and uses its values as the defaults when calling the [`value`](../api/ See [full documentation of `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT`](../api/macros/nlohmann_define_type_intrusive.md). +## `NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(type, member...)` + +This macro is similar to `NLOHMANN_DEFINE_TYPE_INTRUSIVE` except that it defines only the serialization code. This is +useful when the user type does not have a default constructor and only the serialization is required. + +See [full documentation of `NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE`](../api/macros//nlohmann_define_type_intrusive.md). + ## `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 @@ -138,6 +145,13 @@ an object and uses its values as the defaults when calling the [`value`](../api/ See [full documentation of `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT`](../api/macros/nlohmann_define_type_non_intrusive.md). +## `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(type, member...)` + +This macro is similar to `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE` except that it defines only the serialization code. This is +useful when the user type does not have a default constructor and only the serialization is required. + +See [full documentation of `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE`](../api/macros//nlohmann_define_type_non_intrusive.md). + ## `NLOHMANN_JSON_SERIALIZE_ENUM(type, ...)` This macro simplifies the serialization/deserialization of enum types. See diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index faa24b586..5d395c7cf 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -406,6 +406,9 @@ 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) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(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__)) } + /*! @brief macro @def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE @@ -415,6 +418,9 @@ 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_ONLY_SERIALIZE(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__)) } + #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) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index dc58ca118..f4538575c 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2758,6 +2758,9 @@ JSON_HEDLEY_DIAGNOSTIC_POP 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) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(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__)) } + /*! @brief macro @def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE @@ -2767,6 +2770,9 @@ JSON_HEDLEY_DIAGNOSTIC_POP 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_ONLY_SERIALIZE(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__)) } + #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) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } diff --git a/tests/src/unit-udt_macro.cpp b/tests/src/unit-udt_macro.cpp index ac9d9d2e8..c54ecac32 100644 --- a/tests/src/unit-udt_macro.cpp +++ b/tests/src/unit-udt_macro.cpp @@ -279,6 +279,44 @@ class person_with_public_alphabet NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_with_public_alphabet, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z) +class person_without_default_constructor_1 +{ + public: + std::string name; + int age; + + bool operator==(const person_without_default_constructor_1& other) const + { + return name == other.name && age == other.age; + } + + person_without_default_constructor_1(std::string name_, int age_) + : name{std::move(name_)} + , age{age_} + {} + + NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(person_without_default_constructor_1, name, age) +}; + +class person_without_default_constructor_2 +{ + public: + std::string name; + int age; + + bool operator==(const person_without_default_constructor_2& other) const + { + return name == other.name && age == other.age; + } + + person_without_default_constructor_2(std::string name_, int age_) + : name{std::move(name_)} + , age{age_} + {} +}; + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(person_without_default_constructor_2, name, age) + } // namespace persons TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE", T, @@ -412,3 +450,25 @@ TEST_CASE_TEMPLATE("Serialization/deserialization of classes with 26 public/priv } } } + +TEST_CASE_TEMPLATE("Serialization of non-default-constructible classes via NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE", T, + persons::person_without_default_constructor_1, + persons::person_without_default_constructor_2) +{ + SECTION("person") + { + { + // serialization of a single object + T person{"Erik", 1}; + CHECK(json(person).dump() == "{\"age\":1,\"name\":\"Erik\"}"); + + // serialization of a container with objects + std::vector const two_persons + { + {"Erik", 1}, + {"Kyle", 2} + }; + CHECK(json(two_persons).dump() == "[{\"age\":1,\"name\":\"Erik\"},{\"age\":2,\"name\":\"Kyle\"}]"); + } + } +}