From 06b667cbd5a2564cba3a91c1032869fc9a92e1af Mon Sep 17 00:00:00 2001 From: Harinath Nampally Date: Sun, 19 Jan 2025 23:51:58 -0500 Subject: [PATCH 01/13] add strict enum de/serialization macro Signed-off-by: Harinath Nampally --- .../nlohmann_json_serialize_enum _strict.md | 90 +++++++++++++++++++ .../nlohmann_json_deserialize_enum_strict.cpp | 57 ++++++++++++ .../nlohmann_json_serialize_enum_strict.cpp | 56 ++++++++++++ ...nlohmann_json_serialize_enum_strict.output | 1 + docs/mkdocs/docs/features/enum_conversion.md | 20 +++++ include/nlohmann/detail/macro_scope.hpp | 41 +++++++++ single_include/nlohmann/json.hpp | 41 +++++++++ tests/src/unit-conversions.cpp | 90 ++++++++++++++++++- 8 files changed, 395 insertions(+), 1 deletion(-) create mode 100644 docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md create mode 100644 docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.cpp create mode 100644 docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.cpp create mode 100644 docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.output diff --git a/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md b/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md new file mode 100644 index 0000000000..cd09a1a3ec --- /dev/null +++ b/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md @@ -0,0 +1,90 @@ +# NLOHMANN_JSON_SERIALIZE_ENUM_STRICT + +```cpp +#define NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(type, conversion...) +``` + +The `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT` allows to define a user-defined serialization for every enumerator. + +This macro declares strict serialization and deserialization functions (`to_json` and `from_json`) for an enum type. Unlike [`NLOHMANN_JSON_SERIALIZE_ENUM`](nlohmann_json_serialize_enum.md), this macro enforces strict validation and throws errors for unmapped values instead of defaulting to the first enum value. + + +## Parameters + +`type` (in) +: name of the enum to serialize/deserialize + +`conversion` (in) +: a pair of an enumerator and a JSON serialization; arbitrary pairs can be given as a comma-separated list + +## Default definition + +The macro adds two functions to the namespace which take care of the serialization and deserialization: + +```cpp +template +inline void to_json(BasicJsonType& j, const type& e); +template +inline void from_json(const BasicJsonType& j, type& e); +``` + +## Notes + +!!! info "Prerequisites" + + The macro must be used inside the namespace of the enum. + +!!! important "Important notes" + +- If an enum value appears more than once in the mapping, only the first occurrence will be used for serialization, subsequent mappings for the same enum value will be ignored. +- If a JSON value appears more than once in the mapping, only the first occurrence will be used for deserialization, subsequent mappings for the same JSON value will be ignored. +- Unlike `NLOHMANN_JSON_SERIALIZE_ENUM`, this macro enforces strict validation: + - Attempting to serialize an unmapped enum value will throw a `type_error.302` exception + - Attempting to deserialize an unmapped JSON value will throw a `type_error.302` exception + - There is no default value behavior - all values must be explicitly mapped + +## Examples + +??? example "Example 1: Strict serialization" + + The example shows how `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT` enforces strict validation when serializing an enum value that is not in the mapping: + + ```cpp + --8<-- "examples/nlohmann_json_serialize_enum_strict.cpp" + ``` + + Expected output: + + ``` + [json.exception.type_error.302] can't serialize - enum value 3 out of range + ``` + +??? example "Example 2: Strict deserialization" + + The example shows how `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT` enforces strict validation when deserializing a JSON value that is not in the mapping: + + ```cpp + --8<-- "examples/nlohmann_json_deserialize_enum_strict.cpp" + ``` + + Expected output: + + ``` + [json.exception.type_error.302] can't deserialize - invalid json value : "yellow" + ``` + +Both examples demonstrate: + +- Proper error handling using try-catch blocks +- Clear error messages indicating the cause of failure +- No default value behavior - all values must be explicitly mapped +- Exception throwing for unmapped values + +## See also + +- [Specializing enum conversion](../../features/enum_conversion.md) +- [`JSON_DISABLE_ENUM_SERIALIZATION`](json_disable_enum_serialization.md) + +## Version history + +- Added in version 3.11.3. diff --git a/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.cpp b/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.cpp new file mode 100644 index 0000000000..eedd73b8ea --- /dev/null +++ b/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.cpp @@ -0,0 +1,57 @@ +#include +#include + +#ifdef __cpp_exceptions + #undef __cpp_exceptions + #define __cpp_exceptions 1 +#endif + +#ifdef JSON_NOEXCEPTION + #define JSON_NOEXCEPTION 0 +#endif + +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +using json = nlohmann::json; + +namespace ns +{ +enum class Color +{ + red, green, blue +}; + +NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(Color, +{ + { Color::red, "red" }, + { Color::green, "green" }, + { Color::blue, "blue" }, +}) +} + +int main() +{ + + // deserialization + json j_yellow = "yellow"; + try + { + auto yellow = j_yellow.template get(); + std::cout << j_yellow << " -> " << static_cast(yellow) << std::endl; + } + catch (const nlohmann::json::exception& e) + { + std::cout << e.what() << std::endl; + } +} diff --git a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.cpp b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.cpp new file mode 100644 index 0000000000..c99f0216a0 --- /dev/null +++ b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.cpp @@ -0,0 +1,56 @@ +#include +#include + +#ifdef __cpp_exceptions + #undef __cpp_exceptions + #define __cpp_exceptions 1 +#endif + +#ifdef JSON_NOEXCEPTION + #define JSON_NOEXCEPTION 0 +#endif + +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +using json = nlohmann::json; + +namespace ns +{ +enum class Color +{ + red, green, blue, pink +}; + +NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(Color, +{ + { Color::red, "red" }, + { Color::green, "green" }, + { Color::blue, "blue" }, +}) +} + +int main() +{ + // serialization + try + { + json j_red = ns::Color::pink; + auto color = j_red.get(); + std::cout << static_cast(color) << " -> " << j_red << std::endl; + } + catch (const nlohmann::json::exception& e) + { + std::cout << e.what() << std::endl; + } +} diff --git a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.output b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.output new file mode 100644 index 0000000000..70a6334b28 --- /dev/null +++ b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.output @@ -0,0 +1 @@ +[json.exception.type_error.302] can't serialize - enum value 3 out of range diff --git a/docs/mkdocs/docs/features/enum_conversion.md b/docs/mkdocs/docs/features/enum_conversion.md index 1755bca2ad..ae74c86d04 100644 --- a/docs/mkdocs/docs/features/enum_conversion.md +++ b/docs/mkdocs/docs/features/enum_conversion.md @@ -27,6 +27,7 @@ NLOHMANN_JSON_SERIALIZE_ENUM( TaskState, { The [`NLOHMANN_JSON_SERIALIZE_ENUM()` macro](../api/macros/nlohmann_json_serialize_enum.md) declares a set of `to_json()` / `from_json()` functions for type `TaskState` while avoiding repetition and boilerplate serialization code. + ## Usage ```cpp @@ -59,3 +60,22 @@ Other Important points: - If an enum or JSON value is specified more than once in your map, the first matching occurrence from the top of the map will be returned when converting to or from JSON. - To disable the default serialization of enumerators as integers and force a compiler error instead, see [`JSON_DISABLE_ENUM_SERIALIZATION`](../api/macros/json_disable_enum_serialization.md). + +An alternative macro [`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT()` macro](../api/macros/nlohmann_json_serialize_enum.md) can be used when a more strict error handling is preffered, throwing in case of serialization errors instead of defaulting to the first enum value defined in the macro. + +## Usage +```cpp +// example enum type declaration +enum TaskState { + TS_STOPPED, + TS_RUNNING, + TS_COMPLETED, +}; + +// map TaskState values to JSON as strings +NLOHMANN_JSON_SERIALIZE_ENUM_STRICT( TaskState, { + {TS_STOPPED, "stopped"}, + {TS_RUNNING, "running"}, + {TS_COMPLETED, "completed"}, +}) +``` \ No newline at end of file diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index fe825b4431..624e0d64ad 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -242,6 +242,47 @@ e = ((it != std::end(m)) ? it : std::begin(m))->first; \ } +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + /* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on */ \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + if (it == std::end(m)) { \ + auto value = static_cast::type>(e); \ + JSON_THROW(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't serialize - enum value ", std::to_string(value), " out of range"), &j)); \ + } \ + j = it->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + /* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on */ \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + if (it == std::end(m)) \ + JSON_THROW(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't deserialize - invalid json value : ", j.dump()), &j)); \ + e = it->first; \ + } + // Ugly macros to avoid uglier copy-paste when specializing basic_json. They // may be removed in the future once the class is split. diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index cda42a6e34..e1a190f05b 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2608,6 +2608,47 @@ JSON_HEDLEY_DIAGNOSTIC_POP e = ((it != std::end(m)) ? it : std::begin(m))->first; \ } +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + /* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on */ \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + if (it == std::end(m)) { \ + auto value = static_cast::type>(e); \ + JSON_THROW(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't serialize - enum value ", std::to_string(value), " out of range"), &j)); \ + } \ + j = it->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + /* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on */ \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + if (it == std::end(m)) \ + JSON_THROW(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't deserialize - invalid json value : ", j.dump()), &j)); \ + e = it->first; \ + } + // Ugly macros to avoid uglier copy-paste when specializing basic_json. They // may be removed in the future once the class is split. diff --git a/tests/src/unit-conversions.cpp b/tests/src/unit-conversions.cpp index 7aa4e2a468..df64c80fa8 100644 --- a/tests/src/unit-conversions.cpp +++ b/tests/src/unit-conversions.cpp @@ -1657,6 +1657,94 @@ TEST_CASE("JSON to enum mapping") } } +#ifdef __cpp_exceptions + #undef __cpp_exceptions + #define __cpp_exceptions 1 +#endif + +#ifdef JSON_NOEXCEPTION + #define JSON_NOEXCEPTION 0 +#endif + +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +enum class cards_strict {kreuz, pik, herz, karo}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive +NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(cards_strict, +{ + {cards_strict::kreuz, "kreuz"}, + {cards_strict::pik, "pik"}, + {cards_strict::pik, "puk"}, // second entry for cards::puk; will not be used + {cards_strict::herz, "herz"}, + {cards_strict::karo, "karo"} +}) + +enum TaskStateStrict // NOLINT(cert-int09-c,readability-enum-initial-value) +{ + TSS_STOPPED, + TSS_RUNNING, + TSS_COMPLETED, +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive +NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(TaskStateStrict, +{ + {TSS_STOPPED, "stopped"}, + {TSS_RUNNING, "running"}, + {TSS_COMPLETED, "completed"}, +}) + +TEST_CASE("JSON to enum mapping") +{ + SECTION("enum class") + { + // enum -> json + CHECK(json(cards_strict::kreuz) == "kreuz"); + CHECK(json(cards_strict::pik) == "pik"); + CHECK(json(cards_strict::herz) == "herz"); + CHECK(json(cards_strict::karo) == "karo"); + + // json -> enum + CHECK(cards_strict::kreuz == json("kreuz")); + CHECK(cards_strict::pik == json("pik")); + CHECK(cards_strict::herz == json("herz")); + CHECK(cards_strict::karo == json("karo")); + + // invalid json + const json j = "foo"; + CHECK_THROWS_WITH_AS(j.template get(), "[json.exception.type_error.302] can't deserialize - invalid json value : \"foo\"", json::type_error); + } + + SECTION("traditional enum") + { + // enum -> json + CHECK(json(TSS_STOPPED) == "stopped"); + CHECK(json(TSS_RUNNING) == "running"); + CHECK(json(TSS_COMPLETED) == "completed"); + + // json -> enum + CHECK(TSS_STOPPED == json("stopped")); + CHECK(TSS_RUNNING == json("running")); + CHECK(TSS_COMPLETED == json("completed")); + + // invalid json + const json j = "foo"; + CHECK_THROWS_WITH_AS(j.template get(), "[json.exception.type_error.302] can't deserialize - invalid json value : \"foo\"", json::type_error); + } +} + #ifdef JSON_HAS_CPP_17 #ifndef JSON_USE_IMPLICIT_CONVERSIONS TEST_CASE("std::optional") @@ -1725,4 +1813,4 @@ TEST_CASE("std::optional") #ifdef JSON_HAS_CPP_14 #undef JSON_HAS_CPP_14 #endif -DOCTEST_CLANG_SUPPRESS_WARNING_POP +DOCTEST_CLANG_SUPPRESS_WARNING_POP \ No newline at end of file From 4ebebad566f35fd241115786618f67fcbee6ef4a Mon Sep 17 00:00:00 2001 From: Harinath Nampally Date: Mon, 20 Jan 2025 00:40:12 -0500 Subject: [PATCH 02/13] fix ci checks Signed-off-by: Harinath Nampally --- .../nlohmann_json_serialize_enum _strict.md | 19 +++++++++++-------- .../nlohmann_json_deserialize_enum_strict.cpp | 15 --------------- ...ohmann_json_deserialize_enum_strict.output | 1 + .../nlohmann_json_serialize_enum_strict.cpp | 15 --------------- docs/mkdocs/docs/features/enum_conversion.md | 1 - tests/src/unit-conversions.cpp | 8 +------- 6 files changed, 13 insertions(+), 46 deletions(-) create mode 100644 docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.output diff --git a/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md b/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md index cd09a1a3ec..6463cf3660 100644 --- a/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md +++ b/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md @@ -6,8 +6,9 @@ The `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT` allows to define a user-defined serialization for every enumerator. -This macro declares strict serialization and deserialization functions (`to_json` and `from_json`) for an enum type. Unlike [`NLOHMANN_JSON_SERIALIZE_ENUM`](nlohmann_json_serialize_enum.md), this macro enforces strict validation and throws errors for unmapped values instead of defaulting to the first enum value. - +This macro declares strict serialization and deserialization functions (`to_json` and `from_json`) for an enum type. +Unlike [`NLOHMANN_JSON_SERIALIZE_ENUM`](nlohmann_json_serialize_enum.md), this macro enforces strict validation and +throws errors for unmapped values instead of defaulting to the first enum value. ## Parameters @@ -36,12 +37,14 @@ inline void from_json(const BasicJsonType& j, type& e); !!! important "Important notes" -- If an enum value appears more than once in the mapping, only the first occurrence will be used for serialization, subsequent mappings for the same enum value will be ignored. -- If a JSON value appears more than once in the mapping, only the first occurrence will be used for deserialization, subsequent mappings for the same JSON value will be ignored. -- Unlike `NLOHMANN_JSON_SERIALIZE_ENUM`, this macro enforces strict validation: - - Attempting to serialize an unmapped enum value will throw a `type_error.302` exception - - Attempting to deserialize an unmapped JSON value will throw a `type_error.302` exception - - There is no default value behavior - all values must be explicitly mapped + - If an enum value appears more than once in the mapping, only the first occurrence will be used for serialization, + subsequent mappings for the same enum value will be ignored. + - If a JSON value appears more than once in the mapping, only the first occurrence will be used for deserialization, + subsequent mappings for the same JSON value will be ignored. + - Unlike `NLOHMANN_JSON_SERIALIZE_ENUM`, this macro enforces strict validation: + - Attempting to serialize an unmapped enum value will throw a `type_error.302` exception + - Attempting to deserialize an unmapped JSON value will throw a `type_error.302` exception + - There is no default value behavior - all values must be explicitly mapped ## Examples diff --git a/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.cpp b/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.cpp index eedd73b8ea..62a6c48713 100644 --- a/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.cpp @@ -1,26 +1,11 @@ #include #include -#ifdef __cpp_exceptions - #undef __cpp_exceptions - #define __cpp_exceptions 1 -#endif - -#ifdef JSON_NOEXCEPTION - #define JSON_NOEXCEPTION 0 -#endif - #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) #define JSON_THROW(exception) throw exception - #define JSON_TRY try - #define JSON_CATCH(exception) catch(exception) - #define JSON_INTERNAL_CATCH(exception) catch(exception) #else #include #define JSON_THROW(exception) std::abort() - #define JSON_TRY if(true) - #define JSON_CATCH(exception) if(false) - #define JSON_INTERNAL_CATCH(exception) if(false) #endif using json = nlohmann::json; diff --git a/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.output b/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.output new file mode 100644 index 0000000000..20098e9ed1 --- /dev/null +++ b/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.output @@ -0,0 +1 @@ +[json.exception.type_error.302] can't deserialize - invalid json value : "yellow" diff --git a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.cpp b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.cpp index c99f0216a0..d69a548188 100644 --- a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.cpp @@ -1,26 +1,11 @@ #include #include -#ifdef __cpp_exceptions - #undef __cpp_exceptions - #define __cpp_exceptions 1 -#endif - -#ifdef JSON_NOEXCEPTION - #define JSON_NOEXCEPTION 0 -#endif - #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) #define JSON_THROW(exception) throw exception - #define JSON_TRY try - #define JSON_CATCH(exception) catch(exception) - #define JSON_INTERNAL_CATCH(exception) catch(exception) #else #include #define JSON_THROW(exception) std::abort() - #define JSON_TRY if(true) - #define JSON_CATCH(exception) if(false) - #define JSON_INTERNAL_CATCH(exception) if(false) #endif using json = nlohmann::json; diff --git a/docs/mkdocs/docs/features/enum_conversion.md b/docs/mkdocs/docs/features/enum_conversion.md index ae74c86d04..3af83a935a 100644 --- a/docs/mkdocs/docs/features/enum_conversion.md +++ b/docs/mkdocs/docs/features/enum_conversion.md @@ -26,7 +26,6 @@ NLOHMANN_JSON_SERIALIZE_ENUM( TaskState, { The [`NLOHMANN_JSON_SERIALIZE_ENUM()` macro](../api/macros/nlohmann_json_serialize_enum.md) declares a set of `to_json()` / `from_json()` functions for type `TaskState` while avoiding repetition and boilerplate serialization code. - ## Usage diff --git a/tests/src/unit-conversions.cpp b/tests/src/unit-conversions.cpp index df64c80fa8..bdf2315ef4 100644 --- a/tests/src/unit-conversions.cpp +++ b/tests/src/unit-conversions.cpp @@ -1668,15 +1668,9 @@ TEST_CASE("JSON to enum mapping") #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) #define JSON_THROW(exception) throw exception - #define JSON_TRY try - #define JSON_CATCH(exception) catch(exception) - #define JSON_INTERNAL_CATCH(exception) catch(exception) #else #include #define JSON_THROW(exception) std::abort() - #define JSON_TRY if(true) - #define JSON_CATCH(exception) if(false) - #define JSON_INTERNAL_CATCH(exception) if(false) #endif enum class cards_strict {kreuz, pik, herz, karo}; @@ -1813,4 +1807,4 @@ TEST_CASE("std::optional") #ifdef JSON_HAS_CPP_14 #undef JSON_HAS_CPP_14 #endif -DOCTEST_CLANG_SUPPRESS_WARNING_POP \ No newline at end of file +DOCTEST_CLANG_SUPPRESS_WARNING_POP From 0083937dd5b79c696317d74e0fe94ebf94454ded Mon Sep 17 00:00:00 2001 From: Harinath Nampally Date: Mon, 20 Jan 2025 00:57:55 -0500 Subject: [PATCH 03/13] fix few more ci checks Signed-off-by: Harinath Nampally --- .../nlohmann_json_deserialize_enum_strict.cpp | 2 +- .../nlohmann_json_serialize_enum_strict.cpp | 2 +- tests/src/unit-conversions.cpp | 14 ++++---------- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.cpp b/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.cpp index 62a6c48713..6675386d78 100644 --- a/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.cpp @@ -1,7 +1,7 @@ #include #include -#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) +#if !defined(JSON_NOEXCEPTION) && !defined(JSON_THROW_USER) && !defined(JSON_THROW) #define JSON_THROW(exception) throw exception #else #include diff --git a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.cpp b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.cpp index d69a548188..723d7b86f7 100644 --- a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.cpp @@ -1,7 +1,7 @@ #include #include -#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) +#if !defined(JSON_NOEXCEPTION) && !defined(JSON_THROW_USER) && !defined(JSON_THROW) #define JSON_THROW(exception) throw exception #else #include diff --git a/tests/src/unit-conversions.cpp b/tests/src/unit-conversions.cpp index bdf2315ef4..40547e2dc1 100644 --- a/tests/src/unit-conversions.cpp +++ b/tests/src/unit-conversions.cpp @@ -1657,16 +1657,7 @@ TEST_CASE("JSON to enum mapping") } } -#ifdef __cpp_exceptions - #undef __cpp_exceptions - #define __cpp_exceptions 1 -#endif - -#ifdef JSON_NOEXCEPTION - #define JSON_NOEXCEPTION 0 -#endif - -#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) +#if !defined(JSON_NOEXCEPTION) && !defined(JSON_THROW_USER) && !defined(JSON_THROW) #define JSON_THROW(exception) throw exception #else #include @@ -1738,6 +1729,9 @@ TEST_CASE("JSON to enum mapping") CHECK_THROWS_WITH_AS(j.template get(), "[json.exception.type_error.302] can't deserialize - invalid json value : \"foo\"", json::type_error); } } +#if defined(JSON_THROW) + #undef JSON_THROW +#endif #ifdef JSON_HAS_CPP_17 #ifndef JSON_USE_IMPLICIT_CONVERSIONS From c6d9ea0b51fff5ba2135802b15fac7bfd045d870 Mon Sep 17 00:00:00 2001 From: Harinath Nampally Date: Mon, 20 Jan 2025 01:01:55 -0500 Subject: [PATCH 04/13] fix typos Signed-off-by: Harinath Nampally --- .../docs/api/macros/nlohmann_json_serialize_enum _strict.md | 2 +- docs/mkdocs/docs/features/enum_conversion.md | 2 +- include/nlohmann/detail/macro_scope.hpp | 4 ++-- single_include/nlohmann/json.hpp | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md b/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md index 6463cf3660..41c39a0f7f 100644 --- a/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md +++ b/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md @@ -90,4 +90,4 @@ Both examples demonstrate: ## Version history -- Added in version 3.11.3. +- Added in version 3.11.4 diff --git a/docs/mkdocs/docs/features/enum_conversion.md b/docs/mkdocs/docs/features/enum_conversion.md index 3af83a935a..26dc5451b0 100644 --- a/docs/mkdocs/docs/features/enum_conversion.md +++ b/docs/mkdocs/docs/features/enum_conversion.md @@ -26,7 +26,7 @@ NLOHMANN_JSON_SERIALIZE_ENUM( TaskState, { The [`NLOHMANN_JSON_SERIALIZE_ENUM()` macro](../api/macros/nlohmann_json_serialize_enum.md) declares a set of `to_json()` / `from_json()` functions for type `TaskState` while avoiding repetition and boilerplate serialization code. - + ## Usage ```cpp diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 624e0d64ad..ddb8d450be 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -244,8 +244,8 @@ /*! @brief macro to briefly define a mapping between an enum and JSON -@def NLOHMANN_JSON_SERIALIZE_ENUM -@since version 3.4.0 +@def NLOHMANN_JSON_SERIALIZE_ENUM_STRICT +@since version 3.11.4 */ #define NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(ENUM_TYPE, ...) \ template \ diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index e1a190f05b..008da9fc0c 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2610,8 +2610,8 @@ JSON_HEDLEY_DIAGNOSTIC_POP /*! @brief macro to briefly define a mapping between an enum and JSON -@def NLOHMANN_JSON_SERIALIZE_ENUM -@since version 3.4.0 +@def NLOHMANN_JSON_SERIALIZE_ENUM_STRICT +@since version 3.11.4 */ #define NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(ENUM_TYPE, ...) \ template \ From 44449851ebca0ba7a03c344a61812c0abeb708f3 Mon Sep 17 00:00:00 2001 From: Harinath Nampally Date: Sat, 25 Jan 2025 09:31:02 -0500 Subject: [PATCH 05/13] address review comment Signed-off-by: Harinath Nampally --- include/nlohmann/detail/macro_scope.hpp | 32 +++++++++++++++++++++++-- single_include/nlohmann/json.hpp | 32 +++++++++++++++++++++++-- tests/src/unit-conversions.cpp | 10 -------- 3 files changed, 60 insertions(+), 14 deletions(-) diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index ddb8d450be..e9eba73a48 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -17,6 +17,34 @@ #include +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ +template +[[noreturn]] inline void throw_if_exceptions_enabled(T&& exception) +{ +#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) || defined(EXCEPTIONS) + throw std::forward(exception); +#else + std::abort(); +#endif +} +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + // exclude unsupported compilers #if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) #if defined(__clang__) @@ -262,7 +290,7 @@ }); \ if (it == std::end(m)) { \ auto value = static_cast::type>(e); \ - JSON_THROW(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't serialize - enum value ", std::to_string(value), " out of range"), &j)); \ + nlohmann::detail::throw_if_exceptions_enabled(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't serialize - enum value ", std::to_string(value), " out of range"), &j)); \ } \ j = it->second; \ } \ @@ -279,7 +307,7 @@ return ej_pair.second == j; \ }); \ if (it == std::end(m)) \ - JSON_THROW(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't deserialize - invalid json value : ", j.dump()), &j)); \ + nlohmann::detail::throw_if_exceptions_enabled(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't deserialize - invalid json value : ", j.dump()), &j)); \ e = it->first; \ } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 008da9fc0c..f904da159c 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2383,6 +2383,34 @@ JSON_HEDLEY_DIAGNOSTIC_POP // #include +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ +template +[[noreturn]] inline void throw_if_exceptions_enabled(T&& exception) +{ +#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) || defined(EXCEPTIONS) + throw std::forward(exception); +#else + std::abort(); +#endif +} +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + // exclude unsupported compilers #if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) #if defined(__clang__) @@ -2628,7 +2656,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP }); \ if (it == std::end(m)) { \ auto value = static_cast::type>(e); \ - JSON_THROW(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't serialize - enum value ", std::to_string(value), " out of range"), &j)); \ + nlohmann::detail::throw_if_exceptions_enabled(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't serialize - enum value ", std::to_string(value), " out of range"), &j)); \ } \ j = it->second; \ } \ @@ -2645,7 +2673,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP return ej_pair.second == j; \ }); \ if (it == std::end(m)) \ - JSON_THROW(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't deserialize - invalid json value : ", j.dump()), &j)); \ + nlohmann::detail::throw_if_exceptions_enabled(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't deserialize - invalid json value : ", j.dump()), &j)); \ e = it->first; \ } diff --git a/tests/src/unit-conversions.cpp b/tests/src/unit-conversions.cpp index 40547e2dc1..e39e0f3efb 100644 --- a/tests/src/unit-conversions.cpp +++ b/tests/src/unit-conversions.cpp @@ -1657,13 +1657,6 @@ TEST_CASE("JSON to enum mapping") } } -#if !defined(JSON_NOEXCEPTION) && !defined(JSON_THROW_USER) && !defined(JSON_THROW) - #define JSON_THROW(exception) throw exception -#else - #include - #define JSON_THROW(exception) std::abort() -#endif - enum class cards_strict {kreuz, pik, herz, karo}; // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive @@ -1729,9 +1722,6 @@ TEST_CASE("JSON to enum mapping") CHECK_THROWS_WITH_AS(j.template get(), "[json.exception.type_error.302] can't deserialize - invalid json value : \"foo\"", json::type_error); } } -#if defined(JSON_THROW) - #undef JSON_THROW -#endif #ifdef JSON_HAS_CPP_17 #ifndef JSON_USE_IMPLICIT_CONVERSIONS From e7c021f5cb9bebfd8d02e6d991eb2a4c3614af6a Mon Sep 17 00:00:00 2001 From: Harinath Nampally Date: Sat, 25 Jan 2025 09:52:56 -0500 Subject: [PATCH 06/13] fix ununsed variable static check Signed-off-by: Harinath Nampally --- include/nlohmann/detail/macro_scope.hpp | 1 + single_include/nlohmann/json.hpp | 1 + 2 files changed, 2 insertions(+) diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index e9eba73a48..8bf0633fb5 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -26,6 +26,7 @@ template #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) || defined(EXCEPTIONS) throw std::forward(exception); #else + (void)exception; std::abort(); #endif } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index f904da159c..94768c22d9 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2392,6 +2392,7 @@ template #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) || defined(EXCEPTIONS) throw std::forward(exception); #else + (void)exception; std::abort(); #endif } From 32cbaee758d653b7e1060fcd22e667c913b7c92a Mon Sep 17 00:00:00 2001 From: Harinath Nampally Date: Sat, 25 Jan 2025 10:31:04 -0500 Subject: [PATCH 07/13] ci static check fix Signed-off-by: Harinath Nampally --- include/nlohmann/detail/macro_scope.hpp | 3 ++- single_include/nlohmann/json.hpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 8bf0633fb5..f0f7dd571a 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -26,7 +26,8 @@ template #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) || defined(EXCEPTIONS) throw std::forward(exception); #else - (void)exception; + // Forward the exception (even if unused) and abort + std::forward(exception); std::abort(); #endif } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 94768c22d9..8c7163aaf3 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2392,7 +2392,8 @@ template #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) || defined(EXCEPTIONS) throw std::forward(exception); #else - (void)exception; + // Forward the exception (even if unused) and abort + std::forward(exception); std::abort(); #endif } From 2898659d9044b6ea7be7399ef6ed99ee80bd1ec1 Mon Sep 17 00:00:00 2001 From: Harinath Nampally Date: Sun, 26 Jan 2025 16:59:11 -0500 Subject: [PATCH 08/13] address review comment Signed-off-by: Harinath Nampally --- include/nlohmann/detail/macro_scope.hpp | 31 +++++++++++-------------- single_include/nlohmann/json.hpp | 31 +++++++++++-------------- 2 files changed, 26 insertions(+), 36 deletions(-) diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index f0f7dd571a..9389c32b04 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -17,22 +17,6 @@ #include -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ -template -[[noreturn]] inline void throw_if_exceptions_enabled(T&& exception) -{ -#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) || defined(EXCEPTIONS) - throw std::forward(exception); -#else - // Forward the exception (even if unused) and abort - std::forward(exception); - std::abort(); -#endif -} -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END // exclude unsupported compilers #if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) @@ -272,6 +256,17 @@ NLOHMANN_JSON_NAMESPACE_END e = ((it != std::end(m)) ? it : std::begin(m))->first; \ } +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ +template +[[noreturn]] inline void json_throw_from_serialize_macro(T&& exception) +{ + static_cast(exception); + JSON_THROW(std::forward(exception)); +} +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END /*! @brief macro to briefly define a mapping between an enum and JSON @def NLOHMANN_JSON_SERIALIZE_ENUM_STRICT @@ -292,7 +287,7 @@ NLOHMANN_JSON_NAMESPACE_END }); \ if (it == std::end(m)) { \ auto value = static_cast::type>(e); \ - nlohmann::detail::throw_if_exceptions_enabled(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't serialize - enum value ", std::to_string(value), " out of range"), &j)); \ + nlohmann::detail::json_throw_from_serialize_macro(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't serialize - enum value ", std::to_string(value), " out of range"), &j)); \ } \ j = it->second; \ } \ @@ -309,7 +304,7 @@ NLOHMANN_JSON_NAMESPACE_END return ej_pair.second == j; \ }); \ if (it == std::end(m)) \ - nlohmann::detail::throw_if_exceptions_enabled(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't deserialize - invalid json value : ", j.dump()), &j)); \ + nlohmann::detail::json_throw_from_serialize_macro(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't deserialize - invalid json value : ", j.dump()), &j)); \ e = it->first; \ } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 8c7163aaf3..300f21f913 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2383,22 +2383,6 @@ JSON_HEDLEY_DIAGNOSTIC_POP // #include -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ -template -[[noreturn]] inline void throw_if_exceptions_enabled(T&& exception) -{ -#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) || defined(EXCEPTIONS) - throw std::forward(exception); -#else - // Forward the exception (even if unused) and abort - std::forward(exception); - std::abort(); -#endif -} -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END // exclude unsupported compilers #if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) @@ -2638,6 +2622,17 @@ NLOHMANN_JSON_NAMESPACE_END e = ((it != std::end(m)) ? it : std::begin(m))->first; \ } +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ +template +[[noreturn]] inline void json_throw_from_serialize_macro(T&& exception) +{ + static_cast(exception); + JSON_THROW(std::forward(exception)); +} +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END /*! @brief macro to briefly define a mapping between an enum and JSON @def NLOHMANN_JSON_SERIALIZE_ENUM_STRICT @@ -2658,7 +2653,7 @@ NLOHMANN_JSON_NAMESPACE_END }); \ if (it == std::end(m)) { \ auto value = static_cast::type>(e); \ - nlohmann::detail::throw_if_exceptions_enabled(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't serialize - enum value ", std::to_string(value), " out of range"), &j)); \ + nlohmann::detail::json_throw_from_serialize_macro(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't serialize - enum value ", std::to_string(value), " out of range"), &j)); \ } \ j = it->second; \ } \ @@ -2675,7 +2670,7 @@ NLOHMANN_JSON_NAMESPACE_END return ej_pair.second == j; \ }); \ if (it == std::end(m)) \ - nlohmann::detail::throw_if_exceptions_enabled(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't deserialize - invalid json value : ", j.dump()), &j)); \ + nlohmann::detail::json_throw_from_serialize_macro(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't deserialize - invalid json value : ", j.dump()), &j)); \ e = it->first; \ } From 11165ef68d51a04c27954d9cf92cdad30dbda75c Mon Sep 17 00:00:00 2001 From: Harinath Nampally Date: Sun, 26 Jan 2025 18:13:37 -0500 Subject: [PATCH 09/13] address review comments Signed-off-by: Harinath Nampally --- .../nlohmann_json_serialize_enum_strict.cpp | 7 ------- docs/mkdocs/docs/features/enum_conversion.md | 2 +- include/nlohmann/detail/macro_scope.hpp | 16 +--------------- single_include/nlohmann/json.hpp | 16 +--------------- 4 files changed, 3 insertions(+), 38 deletions(-) diff --git a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.cpp b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.cpp index 723d7b86f7..caf2408b61 100644 --- a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.cpp @@ -1,13 +1,6 @@ #include #include -#if !defined(JSON_NOEXCEPTION) && !defined(JSON_THROW_USER) && !defined(JSON_THROW) - #define JSON_THROW(exception) throw exception -#else - #include - #define JSON_THROW(exception) std::abort() -#endif - using json = nlohmann::json; namespace ns diff --git a/docs/mkdocs/docs/features/enum_conversion.md b/docs/mkdocs/docs/features/enum_conversion.md index 26dc5451b0..709abc2eef 100644 --- a/docs/mkdocs/docs/features/enum_conversion.md +++ b/docs/mkdocs/docs/features/enum_conversion.md @@ -60,7 +60,7 @@ Other Important points: map will be returned when converting to or from JSON. - To disable the default serialization of enumerators as integers and force a compiler error instead, see [`JSON_DISABLE_ENUM_SERIALIZATION`](../api/macros/json_disable_enum_serialization.md). -An alternative macro [`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT()` macro](../api/macros/nlohmann_json_serialize_enum.md) can be used when a more strict error handling is preffered, throwing in case of serialization errors instead of defaulting to the first enum value defined in the macro. +An alternative macro [`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT()` macro](../api/macros/nlohmann_json_serialize_enum.md) can be used when a more strict error handling is preferred, throwing in case of serialization errors instead of defaulting to the first enum value defined in the macro. ## Usage ```cpp diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 9389c32b04..ff302dc486 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -17,20 +17,6 @@ #include - -// exclude unsupported compilers -#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) - #if defined(__clang__) - #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 - #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" - #endif - #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) - #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 - #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" - #endif - #endif -#endif - // exclude unsupported compilers #if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) #if defined(__clang__) @@ -182,6 +168,7 @@ #define JSON_INTERNAL_CATCH(exception) catch(exception) #else #include + std::forward(exception); #define JSON_THROW(exception) std::abort() #define JSON_TRY if(true) #define JSON_CATCH(exception) if(false) @@ -262,7 +249,6 @@ namespace detail template [[noreturn]] inline void json_throw_from_serialize_macro(T&& exception) { - static_cast(exception); JSON_THROW(std::forward(exception)); } } // namespace detail diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 300f21f913..de3902b21c 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2383,20 +2383,6 @@ JSON_HEDLEY_DIAGNOSTIC_POP // #include - -// exclude unsupported compilers -#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) - #if defined(__clang__) - #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 - #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" - #endif - #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) - #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 - #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" - #endif - #endif -#endif - // exclude unsupported compilers #if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) #if defined(__clang__) @@ -2548,6 +2534,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP #define JSON_INTERNAL_CATCH(exception) catch(exception) #else #include + std::forward(exception); #define JSON_THROW(exception) std::abort() #define JSON_TRY if(true) #define JSON_CATCH(exception) if(false) @@ -2628,7 +2615,6 @@ namespace detail template [[noreturn]] inline void json_throw_from_serialize_macro(T&& exception) { - static_cast(exception); JSON_THROW(std::forward(exception)); } } // namespace detail From 0cda97237756284ddd170ef44b4d11f0e1f56e68 Mon Sep 17 00:00:00 2001 From: Harinath Nampally Date: Sun, 26 Jan 2025 18:33:04 -0500 Subject: [PATCH 10/13] fix ci check Signed-off-by: Harinath Nampally --- .../examples/nlohmann_json_deserialize_enum_strict.cpp | 7 ------- include/nlohmann/detail/macro_scope.hpp | 9 +++++++-- single_include/nlohmann/json.hpp | 9 +++++++-- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.cpp b/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.cpp index 6675386d78..bd93cf412d 100644 --- a/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.cpp @@ -1,13 +1,6 @@ #include #include -#if !defined(JSON_NOEXCEPTION) && !defined(JSON_THROW_USER) && !defined(JSON_THROW) - #define JSON_THROW(exception) throw exception -#else - #include - #define JSON_THROW(exception) std::abort() -#endif - using json = nlohmann::json; namespace ns diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index ff302dc486..8bf6119712 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -168,7 +168,6 @@ #define JSON_INTERNAL_CATCH(exception) catch(exception) #else #include - std::forward(exception); #define JSON_THROW(exception) std::abort() #define JSON_TRY if(true) #define JSON_CATCH(exception) if(false) @@ -249,7 +248,13 @@ namespace detail template [[noreturn]] inline void json_throw_from_serialize_macro(T&& exception) { - JSON_THROW(std::forward(exception)); +#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) || defined(EXCEPTIONS) + throw std::forward(exception); +#else + // Forward the exception (even if unused) and abort + std::forward(exception); + std::abort(); +#endif } } // namespace detail NLOHMANN_JSON_NAMESPACE_END diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index de3902b21c..3559d49661 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2534,7 +2534,6 @@ JSON_HEDLEY_DIAGNOSTIC_POP #define JSON_INTERNAL_CATCH(exception) catch(exception) #else #include - std::forward(exception); #define JSON_THROW(exception) std::abort() #define JSON_TRY if(true) #define JSON_CATCH(exception) if(false) @@ -2615,7 +2614,13 @@ namespace detail template [[noreturn]] inline void json_throw_from_serialize_macro(T&& exception) { - JSON_THROW(std::forward(exception)); +#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) || defined(EXCEPTIONS) + throw std::forward(exception); +#else + // Forward the exception (even if unused) and abort + std::forward(exception); + std::abort(); +#endif } } // namespace detail NLOHMANN_JSON_NAMESPACE_END From e25fc042ff1f105a2a54f75a85c5b1b7368774da Mon Sep 17 00:00:00 2001 From: Harinath Nampally Date: Mon, 27 Jan 2025 21:36:46 -0500 Subject: [PATCH 11/13] address review comments Signed-off-by: Harinath Nampally --- .../docs/api/macros/nlohmann_json_serialize_enum _strict.md | 4 ++-- .../examples/nlohmann_json_serialize_enum_strict.output | 2 +- include/nlohmann/detail/macro_scope.hpp | 6 +++--- single_include/nlohmann/json.hpp | 6 +++--- tests/src/unit-conversions.cpp | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md b/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md index 41c39a0f7f..180f4ca86f 100644 --- a/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md +++ b/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md @@ -59,7 +59,7 @@ inline void from_json(const BasicJsonType& j, type& e); Expected output: ``` - [json.exception.type_error.302] can't serialize - enum value 3 out of range + [json.exception.type_error.302] serialization failed: enum value 3 is out of range ``` ??? example "Example 2: Strict deserialization" @@ -73,7 +73,7 @@ inline void from_json(const BasicJsonType& j, type& e); Expected output: ``` - [json.exception.type_error.302] can't deserialize - invalid json value : "yellow" + [json.exception.type_error.302] deserialization failed: invalid JSON value "yellow" ``` Both examples demonstrate: diff --git a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.output b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.output index 70a6334b28..cdd1ff85cc 100644 --- a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.output +++ b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.output @@ -1 +1 @@ -[json.exception.type_error.302] can't serialize - enum value 3 out of range +[json.exception.type_error.302] serialization failed: enum value 3 is out of range diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 8bf6119712..1ed9dd1142 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -248,7 +248,7 @@ namespace detail template [[noreturn]] inline void json_throw_from_serialize_macro(T&& exception) { -#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) || defined(EXCEPTIONS) +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) throw std::forward(exception); #else // Forward the exception (even if unused) and abort @@ -278,7 +278,7 @@ NLOHMANN_JSON_NAMESPACE_END }); \ if (it == std::end(m)) { \ auto value = static_cast::type>(e); \ - nlohmann::detail::json_throw_from_serialize_macro(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't serialize - enum value ", std::to_string(value), " out of range"), &j)); \ + nlohmann::detail::json_throw_from_serialize_macro(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("serialization failed: enum value ", std::to_string(value), " is out of range"), &j)); \ } \ j = it->second; \ } \ @@ -295,7 +295,7 @@ NLOHMANN_JSON_NAMESPACE_END return ej_pair.second == j; \ }); \ if (it == std::end(m)) \ - nlohmann::detail::json_throw_from_serialize_macro(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't deserialize - invalid json value : ", j.dump()), &j)); \ + nlohmann::detail::json_throw_from_serialize_macro(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("deserialization failed: invalid JSON value ", j.dump()), &j)); \ e = it->first; \ } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 3559d49661..d1d3443f77 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2614,7 +2614,7 @@ namespace detail template [[noreturn]] inline void json_throw_from_serialize_macro(T&& exception) { -#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) || defined(EXCEPTIONS) +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) throw std::forward(exception); #else // Forward the exception (even if unused) and abort @@ -2644,7 +2644,7 @@ NLOHMANN_JSON_NAMESPACE_END }); \ if (it == std::end(m)) { \ auto value = static_cast::type>(e); \ - nlohmann::detail::json_throw_from_serialize_macro(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't serialize - enum value ", std::to_string(value), " out of range"), &j)); \ + nlohmann::detail::json_throw_from_serialize_macro(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("serialization failed: enum value ", std::to_string(value), " is out of range"), &j)); \ } \ j = it->second; \ } \ @@ -2661,7 +2661,7 @@ NLOHMANN_JSON_NAMESPACE_END return ej_pair.second == j; \ }); \ if (it == std::end(m)) \ - nlohmann::detail::json_throw_from_serialize_macro(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("can't deserialize - invalid json value : ", j.dump()), &j)); \ + nlohmann::detail::json_throw_from_serialize_macro(nlohmann::detail::type_error::create(302, nlohmann::detail::concat("deserialization failed: invalid JSON value ", j.dump()), &j)); \ e = it->first; \ } diff --git a/tests/src/unit-conversions.cpp b/tests/src/unit-conversions.cpp index e39e0f3efb..6818506165 100644 --- a/tests/src/unit-conversions.cpp +++ b/tests/src/unit-conversions.cpp @@ -1702,7 +1702,7 @@ TEST_CASE("JSON to enum mapping") // invalid json const json j = "foo"; - CHECK_THROWS_WITH_AS(j.template get(), "[json.exception.type_error.302] can't deserialize - invalid json value : \"foo\"", json::type_error); + CHECK_THROWS_WITH_AS(j.template get(), "[json.exception.type_error.302] deserialization failed: invalid JSON value \"foo\"", json::type_error); } SECTION("traditional enum") @@ -1719,7 +1719,7 @@ TEST_CASE("JSON to enum mapping") // invalid json const json j = "foo"; - CHECK_THROWS_WITH_AS(j.template get(), "[json.exception.type_error.302] can't deserialize - invalid json value : \"foo\"", json::type_error); + CHECK_THROWS_WITH_AS(j.template get(), "[json.exception.type_error.302] deserialization failed: invalid JSON value \"foo\"", json::type_error); } } From 5f7d41c0a439f517702b646ec9a7f30d46b1a01f Mon Sep 17 00:00:00 2001 From: Harinath Nampally Date: Tue, 28 Jan 2025 21:20:36 -0500 Subject: [PATCH 12/13] further documentation improvements Signed-off-by: Harinath Nampally --- .../macros/nlohmann_json_serialize_enum _strict.md | 11 +++++------ .../nlohmann_json_deserialize_enum_strict.output | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md b/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md index 180f4ca86f..bbb5b63e43 100644 --- a/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md +++ b/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md @@ -4,7 +4,7 @@ #define NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(type, conversion...) ``` -The `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT` allows to define a user-defined serialization for every enumerator. +The `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT` macro allows defining a user-defined serialization for every enumerator. This macro declares strict serialization and deserialization functions (`to_json` and `from_json`) for an enum type. Unlike [`NLOHMANN_JSON_SERIALIZE_ENUM`](nlohmann_json_serialize_enum.md), this macro enforces strict validation and @@ -16,7 +16,7 @@ throws errors for unmapped values instead of defaulting to the first enum value. : name of the enum to serialize/deserialize `conversion` (in) -: a pair of an enumerator and a JSON serialization; arbitrary pairs can be given as a comma-separated list +: A list of parameters alternating between an enumerator value and a string to use in the JSON serialization. ## Default definition @@ -37,10 +37,9 @@ inline void from_json(const BasicJsonType& j, type& e); !!! important "Important notes" - - If an enum value appears more than once in the mapping, only the first occurrence will be used for serialization, - subsequent mappings for the same enum value will be ignored. - - If a JSON value appears more than once in the mapping, only the first occurrence will be used for deserialization, - subsequent mappings for the same JSON value will be ignored. + - Duplicate enum or JSON values in the mapping are not supported. Only the first occurrence will be used, + and subsequent mappings will be ignored. + This behavior is currently tolerated but may become an error in future versions. - Unlike `NLOHMANN_JSON_SERIALIZE_ENUM`, this macro enforces strict validation: - Attempting to serialize an unmapped enum value will throw a `type_error.302` exception - Attempting to deserialize an unmapped JSON value will throw a `type_error.302` exception diff --git a/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.output b/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.output index 20098e9ed1..5b1d3f0623 100644 --- a/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.output +++ b/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.output @@ -1 +1 @@ -[json.exception.type_error.302] can't deserialize - invalid json value : "yellow" +[json.exception.type_error.302] deserialization failed: invalid JSON value "yellow" From 6747555bc736852f487487d670ca4b4bf521b6ea Mon Sep 17 00:00:00 2001 From: Harinath Nampally Date: Sun, 2 Feb 2025 21:57:02 -0500 Subject: [PATCH 13/13] address review comments Signed-off-by: Harinath Nampally --- ..._enum _strict.md => nlohmann_json_serialize_enum_strict.md} | 2 ++ .../docs/examples/nlohmann_json_deserialize_enum_strict.cpp | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) rename docs/mkdocs/docs/api/macros/{nlohmann_json_serialize_enum _strict.md => nlohmann_json_serialize_enum_strict.md} (95%) diff --git a/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md b/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum_strict.md similarity index 95% rename from docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md rename to docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum_strict.md index bbb5b63e43..3d026b3c97 100644 --- a/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum _strict.md +++ b/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum_strict.md @@ -58,6 +58,7 @@ inline void from_json(const BasicJsonType& j, type& e); Expected output: ``` + --8<-- "examples/nlohmann_json_serialize_enum_strict.output" [json.exception.type_error.302] serialization failed: enum value 3 is out of range ``` @@ -72,6 +73,7 @@ inline void from_json(const BasicJsonType& j, type& e); Expected output: ``` + --8<-- "examples/nlohmann_json_deserialize_enum_strict.output" [json.exception.type_error.302] deserialization failed: invalid JSON value "yellow" ``` diff --git a/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.cpp b/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.cpp index bd93cf412d..beb73e7e71 100644 --- a/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.cpp +++ b/docs/mkdocs/docs/examples/nlohmann_json_deserialize_enum_strict.cpp @@ -20,9 +20,8 @@ NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(Color, int main() { - - // deserialization json j_yellow = "yellow"; + // deserialization try { auto yellow = j_yellow.template get();