From be589be287f09abbbd8bf1e437d23317af97ce65 Mon Sep 17 00:00:00 2001 From: Caillin Nugent Date: Mon, 27 Apr 2026 15:15:37 +1000 Subject: [PATCH 01/14] Added NLOHNMANN_JSON_SERIALIZE_ENUM_STRICT - duplicate of NLOHMANN_JSON_SERIALIZE_ENUM Signed-off-by: Caillin Nugent --- include/nlohmann/detail/macro_scope.hpp | 37 +++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index afb400c90b..736523c303 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -253,6 +253,43 @@ e = ((it != std::end(m)) ? it : std::begin(m))->first; \ } +/*! +@brief macro to briefly define a mapping between an enum and JSON with exception + on invalid input +@def NLOHMANN_JSON_SERIALIZE_ENUM_STRICT +@since version 3.12.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; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->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; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + + // Ugly macros to avoid uglier copy-paste when specializing basic_json. They // may be removed in the future once the class is split. From 16e2669c4df6d3e02e5dbc7419fe29ff78c87944 Mon Sep 17 00:00:00 2001 From: Caillin Nugent Date: Mon, 27 Apr 2026 17:28:42 +1000 Subject: [PATCH 02/14] Added failing tests for NLOHMANN_JSON_SERIALIZE_ENUM_STRICT Signed-off-by: Caillin Nugent --- tests/src/unit-conversions.cpp | 72 ++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/tests/src/unit-conversions.cpp b/tests/src/unit-conversions.cpp index 81b8608fb8..b9ba697ec5 100644 --- a/tests/src/unit-conversions.cpp +++ b/tests/src/unit-conversions.cpp @@ -1657,6 +1657,78 @@ TEST_CASE("JSON to enum mapping") } } +enum class strict_cards {kreuz, pik, herz, karo}; + +// NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive +NLOHMANN_JSON_SERIALIZE_ENUM(strict_cards, +{ + {strict_cards::kreuz, "kreuz"}, + {strict_cards::pik, "pik"}, + {strict_cards::pik, "puk"}, // second entry for cards::puk; will not be used + {strict_cards::herz, "herz"}, + {strict_cards::karo, "karo"} +}) + +enum StrictTaskState // NOLINT(cert-int09-c,readability-enum-initial-value,cppcoreguidelines-use-enum-class) +{ + STRICT_TS_STOPPED, + STRICT_TS_RUNNING, + STRICT_TS_COMPLETED, + STRICT_TS_INVALID = -1, +}; + +// NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive +NLOHMANN_JSON_SERIALIZE_ENUM(StrictTaskState, +{ + {STRICT_TS_INVALID, nullptr}, + {STRICT_TS_STOPPED, "stopped"}, + {STRICT_TS_RUNNING, "running"}, + {STRICT_TS_COMPLETED, "completed"}, +}) + +TEST_CASE("Strict JSON to enum mapping") +{ + SECTION("enum class") + { + // enum -> json + CHECK(json(strict_cards::kreuz) == "kreuz"); + CHECK(json(strict_cards::pik) == "pik"); + CHECK(json(strict_cards::herz) == "herz"); + CHECK(json(strict_cards::karo) == "karo"); + + // json -> enum + CHECK(strict_cards::kreuz == json("kreuz")); + CHECK(strict_cards::pik == json("pik")); + CHECK(strict_cards::herz == json("herz")); + CHECK(strict_cards::karo == json("karo")); + + // invalid json -> exception thrown + CHECK(strict_cards::kreuz == json("what?").get()); + json _; + CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.403] enum value 'what?' out of range", json::out_of_range&); + } + + SECTION("traditional enum") + { + // enum -> json + CHECK(json(STRICT_TS_STOPPED) == "stopped"); + CHECK(json(STRICT_TS_RUNNING) == "running"); + CHECK(json(STRICT_TS_COMPLETED) == "completed"); + CHECK(json(STRICT_TS_INVALID) == json()); + + // json -> enum + CHECK(STRICT_TS_STOPPED == json("stopped")); + CHECK(STRICT_TS_RUNNING == json("running")); + CHECK(STRICT_TS_COMPLETED == json("completed")); + CHECK(STRICT_TS_INVALID == json()); + + // invalid json -> exception thrown + json _; + CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.403] enum value 'what?' out of range", json::out_of_range&); + } +} + + #ifdef JSON_HAS_CPP_17 #if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM TEST_CASE("std::filesystem::path") From ff66dad145ebc3a75ac576a228802d73b0559052 Mon Sep 17 00:00:00 2001 From: Caillin Nugent Date: Mon, 27 Apr 2026 20:53:46 +1000 Subject: [PATCH 03/14] modified NLOHMANN_JSON_SERIALIZE_STRICT to throw Signed-off-by: Caillin Nugent --- include/nlohmann/detail/macro_scope.hpp | 6 ++++-- tests/src/unit-conversions.cpp | 9 ++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 736523c303..73044e4600 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -272,7 +272,8 @@ { \ return ej_pair.first == e; \ }); \ - j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + if (it != std::end(m)) j = it->second; \ + else throw nlohmann::detail::out_of_range::create(403, "enum value out of range", nullptr);\ } \ template \ inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ @@ -286,7 +287,8 @@ { \ return ej_pair.second == j; \ }); \ - e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + if (it != std::end(m)) e = it->first; \ + else throw nlohmann::detail::out_of_range::create(403, "enum value out of range", nullptr);\ } diff --git a/tests/src/unit-conversions.cpp b/tests/src/unit-conversions.cpp index b9ba697ec5..a73102b7c3 100644 --- a/tests/src/unit-conversions.cpp +++ b/tests/src/unit-conversions.cpp @@ -1660,7 +1660,7 @@ TEST_CASE("JSON to enum mapping") enum class strict_cards {kreuz, pik, herz, karo}; // NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive -NLOHMANN_JSON_SERIALIZE_ENUM(strict_cards, +NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(strict_cards, { {strict_cards::kreuz, "kreuz"}, {strict_cards::pik, "pik"}, @@ -1678,7 +1678,7 @@ enum StrictTaskState // NOLINT(cert-int09-c,readability-enum-initial-value,cppco }; // NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive -NLOHMANN_JSON_SERIALIZE_ENUM(StrictTaskState, +NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(StrictTaskState, { {STRICT_TS_INVALID, nullptr}, {STRICT_TS_STOPPED, "stopped"}, @@ -1703,9 +1703,8 @@ TEST_CASE("Strict JSON to enum mapping") CHECK(strict_cards::karo == json("karo")); // invalid json -> exception thrown - CHECK(strict_cards::kreuz == json("what?").get()); json _; - CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.403] enum value 'what?' out of range", json::out_of_range&); + CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.403] enum value out of range", json::out_of_range&); } SECTION("traditional enum") @@ -1724,7 +1723,7 @@ TEST_CASE("Strict JSON to enum mapping") // invalid json -> exception thrown json _; - CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.403] enum value 'what?' out of range", json::out_of_range&); + CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.403] enum value out of range", json::out_of_range&); } } From 768eb663fc5aad3b37ba2426a922030880dd25ec Mon Sep 17 00:00:00 2001 From: Caillin Nugent Date: Mon, 27 Apr 2026 21:13:59 +1000 Subject: [PATCH 04/14] added documentation and changed readme to include NLOHMANN_JSON_SERIALIZE_ENUM_STRICT Signed-off-by: Caillin Nugent --- README.md | 2 +- docs/mkdocs/docs/features/enum_conversion.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 658f932202..af1da864dd 100644 --- a/README.md +++ b/README.md @@ -1074,7 +1074,7 @@ Just as in [Arbitrary Type Conversions](#arbitrary-types-conversions) above, Other Important points: -- When using `get()`, undefined JSON values will default to the first pair specified in your map. Select this default pair carefully. +- When using `get()`, undefined JSON values will default to the first pair specified in your map. Select this default pair carefully. If you desire an exception in this circumstance use `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT()` which behaves identically except for throwing an exception on unrecognized values. - 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. ### Binary formats (BSON, CBOR, MessagePack, UBJSON, and BJData) diff --git a/docs/mkdocs/docs/features/enum_conversion.md b/docs/mkdocs/docs/features/enum_conversion.md index bd3977d919..db43df8e74 100644 --- a/docs/mkdocs/docs/features/enum_conversion.md +++ b/docs/mkdocs/docs/features/enum_conversion.md @@ -55,7 +55,8 @@ Just as in [Arbitrary Type Conversions](arbitrary_types.md) above, Other Important points: - When using `get()`, undefined JSON values will default to the first pair specified in your map. Select this - default pair carefully. + default pair carefully. If you desire an exception in this circumstance use `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT()` + which behaves identically except for throwing an exception on unrecognised values. - 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). From 645367445c29849bbd2184f85c22c9be9834ac3d Mon Sep 17 00:00:00 2001 From: Caillin Nugent Date: Mon, 27 Apr 2026 21:16:16 +1000 Subject: [PATCH 05/14] ran amalgamate Signed-off-by: Caillin Nugent --- include/nlohmann/detail/macro_scope.hpp | 8 ++--- single_include/nlohmann/json.hpp | 39 +++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 73044e4600..5a43774a27 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -272,8 +272,8 @@ { \ return ej_pair.first == e; \ }); \ - if (it != std::end(m)) j = it->second; \ - else throw nlohmann::detail::out_of_range::create(403, "enum value out of range", nullptr);\ + if (it != std::end(m)) j = it->second; \ + else throw nlohmann::detail::out_of_range::create(403, "enum value out of range", nullptr);\ } \ template \ inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ @@ -287,8 +287,8 @@ { \ return ej_pair.second == j; \ }); \ - if (it != std::end(m)) e = it->first; \ - else throw nlohmann::detail::out_of_range::create(403, "enum value out of range", nullptr);\ + if (it != std::end(m)) e = it->first; \ + else throw nlohmann::detail::out_of_range::create(403, "enum value out of range", nullptr);\ } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index e2bb8517b6..53fd416288 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2617,6 +2617,45 @@ 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 with exception + on invalid input +@def NLOHMANN_JSON_SERIALIZE_ENUM_STRICT +@since version 3.12.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)) j = it->second; \ + else throw nlohmann::detail::out_of_range::create(403, "enum value out of range", nullptr);\ + } \ + 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)) e = it->first; \ + else throw nlohmann::detail::out_of_range::create(403, "enum value out of range", nullptr);\ + } + + // Ugly macros to avoid uglier copy-paste when specializing basic_json. They // may be removed in the future once the class is split. From 1cc9a04b279943595524474807b9ea1e3c220576 Mon Sep 17 00:00:00 2001 From: Caillin Nugent Date: Tue, 12 May 2026 08:25:21 +1000 Subject: [PATCH 06/14] docs(macros): add page for JSON_SERIALIZE_ENUM_STRICT - added page to nav - added links to new page where appropriate Signed-off-by: Caillin Nugent --- docs/mkdocs/docs/api/macros/index.md | 1 + .../macros/nlohmann_json_serialize_enum.md | 1 + .../nlohmann_json_serialize_enum_strict.md | 86 +++++++++++++++++++ .../nlohmann_json_serialize_enum_strict.cpp | 52 +++++++++++ ...nlohmann_json_serialize_enum_strict.output | 2 + .../nlohmann_json_serialize_enum_strict_2.cpp | 33 +++++++ ...ohmann_json_serialize_enum_strict_2.output | 3 + docs/mkdocs/docs/features/enum_conversion.md | 2 +- docs/mkdocs/mkdocs.yml | 1 + 9 files changed, 180 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_serialize_enum_strict.cpp create mode 100644 docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.output create mode 100644 docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_2.cpp create mode 100644 docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_2.output diff --git a/docs/mkdocs/docs/api/macros/index.md b/docs/mkdocs/docs/api/macros/index.md index 59e4b3d280..ad388e5503 100644 --- a/docs/mkdocs/docs/api/macros/index.md +++ b/docs/mkdocs/docs/api/macros/index.md @@ -53,6 +53,7 @@ header. See also the [macro overview page](../../features/macros.md). ### Enums - [**NLOHMANN_JSON_SERIALIZE_ENUM**](nlohmann_json_serialize_enum.md) - serialize/deserialize an enum +- [**NLOHMANN_JSON_SERIALIZE_ENUM_STRICT**](nlohmann_json_serialize_enum_strict.md) - serialize/deserialize an enum with exceptions ### Classes and structs diff --git a/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum.md b/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum.md index fba6b217ab..915a20a5f5 100644 --- a/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum.md +++ b/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum.md @@ -78,6 +78,7 @@ inline void from_json(const BasicJsonType& j, type& e); ## See also - [Specializing enum conversion](../../features/enum_conversion.md) +- [`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT`](./nlohmann_json_serialize_enum_strict.md) - [`JSON_DISABLE_ENUM_SERIALIZATION`](json_disable_enum_serialization.md) ## Version history 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..fed2171ff8 --- /dev/null +++ b/docs/mkdocs/docs/api/macros/nlohmann_json_serialize_enum_strict.md @@ -0,0 +1,86 @@ +# NLOHMANN_JSON_SERIALIZE_ENUM_STRICT + +```cpp +#define NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(type, conversion...) +``` + +By default, enum values are serialized to JSON as integers. In some cases, this could result in undesired behavior. If +an enum is modified or re-ordered after data has been serialized to JSON, the later deserialized JSON data may be +undefined or a different enum value than was originally intended. + +`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT` allows to define a user-defined serialization for every enumerator that +throws an exception on undefined input. + +## 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" + + - When using [`get()`](../basic_json/get.md), undefined JSON values will throw an exception. + - If an enum or JSON value is specified in multiple conversions, the first matching conversion from the top of the + list will be returned when converting to or from JSON. See example 2 below. + +## Examples + +??? example "Example 1: Basic usage" + + The example shows how `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT` can be used to serialize/deserialize both classical enums and + C++11 enum classes: + + ```cpp hl_lines="16 17 18 19 20 21 22 29 30 31 32 33" + --8<-- "examples/nlohmann_json_serialize_enum_strict.cpp" + ``` + + Output: + + ```json + --8<-- "examples/nlohmann_json_serialize_enum_strict.output" + ``` + +??? example "Example 2: Multiple conversions for one enumerator" + + The example shows how to use multiple conversions for a single enumerator. In the example, `Color::red` will always + be *serialized* to `"red"`, because the first occurring conversion. The second conversion, however, offers an + alternative *deserialization* from `"rot"` to `Color::red`. + + ```cpp hl_lines="17" + --8<-- "examples/nlohmann_json_serialize_enum_strict_2.cpp" + ``` + + Output: + + ```json + --8<-- "examples/nlohmann_json_serialize_enum_strict_2.output" + ``` + +## See also + +- [Specializing enum conversion](../../features/enum_conversion.md) +- [`NLOHMANN_JSON_SERIALIZE_ENUM`](./nlohmann_json_serialize_enum.md) +- [`JSON_DISABLE_ENUM_SERIALIZATION`](json_disable_enum_serialization.md) + +## Version history + +Added in version 3.12.0. 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..fa2572dec1 --- /dev/null +++ b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.cpp @@ -0,0 +1,52 @@ +#include +#include + +using json = nlohmann::json; + +namespace ns +{ +enum TaskState +{ + TS_STOPPED, + TS_RUNNING, + TS_COMPLETED, + TS_INVALID = -1 +}; + +NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(TaskState, +{ + { TS_INVALID, nullptr }, + { TS_STOPPED, "stopped" }, + { TS_RUNNING, "running" }, + { TS_COMPLETED, "completed" } +}) + +enum class Color +{ + red, green, blue, unknown +}; + +NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(Color, +{ + { Color::unknown, "unknown" }, { Color::red, "red" }, + { Color::green, "green" }, { Color::blue, "blue" } +}) +} // namespace ns + +int main() +{ + // serialization + json j_stopped = ns::TS_STOPPED; + json j_red = ns::Color::red; + std::cout << "ns::TS_STOPPED -> " << j_stopped + << ", ns::Color::red -> " << j_red << std::endl; + + // deserialization + json j_running = "running"; + json j_blue = "blue"; + auto running = j_running.get(); + auto blue = j_blue.get(); + std::cout << j_running << " -> " << running + << ", " << j_blue << " -> " << static_cast(blue) << 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..59c4ff70ab --- /dev/null +++ b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict.output @@ -0,0 +1,2 @@ +ns::TS_STOPPED -> "stopped", ns::Color::red -> "red" +"running" -> 1, "blue" -> 2 diff --git a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_2.cpp b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_2.cpp new file mode 100644 index 0000000000..7f766b7a93 --- /dev/null +++ b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_2.cpp @@ -0,0 +1,33 @@ +#include +#include + +using json = nlohmann::json; + +namespace ns +{ +enum class Color +{ + red, green, blue, unknown +}; + +NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(Color, +{ + { Color::unknown, "unknown" }, { Color::red, "red" }, + { Color::green, "green" }, { Color::blue, "blue" }, + { Color::red, "rot" } // a second conversion for Color::red +}) +} + +int main() +{ + // serialization + json j_red = ns::Color::red; + std::cout << static_cast(ns::Color::red) << " -> " << j_red << std::endl; + + // deserialization + json j_rot = "rot"; + auto rot = j_rot.get(); + auto red = j_red.get(); + std::cout << j_rot << " -> " << static_cast(rot) << std::endl; + std::cout << j_red << " -> " << static_cast(red) << std::endl; +} diff --git a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_2.output b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_2.output new file mode 100644 index 0000000000..5dec31b4ad --- /dev/null +++ b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_2.output @@ -0,0 +1,3 @@ +0 -> "red" +"rot" -> 0 +"red" -> 0 diff --git a/docs/mkdocs/docs/features/enum_conversion.md b/docs/mkdocs/docs/features/enum_conversion.md index db43df8e74..89675c2795 100644 --- a/docs/mkdocs/docs/features/enum_conversion.md +++ b/docs/mkdocs/docs/features/enum_conversion.md @@ -55,7 +55,7 @@ Just as in [Arbitrary Type Conversions](arbitrary_types.md) above, Other Important points: - When using `get()`, undefined JSON values will default to the first pair specified in your map. Select this - default pair carefully. If you desire an exception in this circumstance use `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT()` + default pair carefully. If you desire an exception in this circumstance use [`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT()`](../api/macros/nlohmann_json_serialize_enum_strict.md) which behaves identically except for throwing an exception on unrecognised values. - 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. diff --git a/docs/mkdocs/mkdocs.yml b/docs/mkdocs/mkdocs.yml index ea089c7ea5..56dfa6e7a0 100644 --- a/docs/mkdocs/mkdocs.yml +++ b/docs/mkdocs/mkdocs.yml @@ -290,6 +290,7 @@ nav: - 'NLOHMANN_JSON_NAMESPACE_BEGIN, NLOHMANN_JSON_NAMESPACE_END': api/macros/nlohmann_json_namespace_begin.md - 'NLOHMANN_JSON_NAMESPACE_NO_VERSION': api/macros/nlohmann_json_namespace_no_version.md - 'NLOHMANN_JSON_SERIALIZE_ENUM': api/macros/nlohmann_json_serialize_enum.md + - 'NLOHMANN_JSON_SERIALIZE_ENUM_STRICT': api/macros/nlohmann_json_serialize_enum_strict.md - 'NLOHMANN_JSON_VERSION_MAJOR, NLOHMANN_JSON_VERSION_MINOR, NLOHMANN_JSON_VERSION_PATCH': api/macros/nlohmann_json_version_major.md - Community: - community/index.md From 6d9e0a8a169552f8ff8523019bd5ccb0ebe645a2 Mon Sep 17 00:00:00 2001 From: Caillin Nugent Date: Wed, 13 May 2026 13:59:05 +1000 Subject: [PATCH 07/14] refactor(macros): make JSON_SERIALIZE_ENUM_STRICT use JSON_THROW - added templated wrapper function to fix scope error in calling JSON_THROW Signed-off-by: Caillin Nugent --- include/nlohmann/detail/macro_scope.hpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 5a43774a27..edcb5b1163 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -253,6 +253,19 @@ e = ((it != std::end(m)) ? it : std::begin(m))->first; \ } + + +/*! +@brief function to wrap JSON_THROW_MACRO - NLOHMANN_SERIALIZE_ENUM_STRICT has a + compilation warning about there being no arguments to JSON_THROW that depend on + template arguments otherwise +*/ +template +void templated_json_throw(ExceptionType exception) +{ + JSON_THROW(exception); +} + /*! @brief macro to briefly define a mapping between an enum and JSON with exception on invalid input @@ -273,7 +286,7 @@ return ej_pair.first == e; \ }); \ if (it != std::end(m)) j = it->second; \ - else throw nlohmann::detail::out_of_range::create(403, "enum value out of range", nullptr);\ + else templated_json_throw(nlohmann::detail::out_of_range::create(403,"enum value out of range",nullptr)); \ } \ template \ inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ @@ -288,7 +301,7 @@ return ej_pair.second == j; \ }); \ if (it != std::end(m)) e = it->first; \ - else throw nlohmann::detail::out_of_range::create(403, "enum value out of range", nullptr);\ + else templated_json_throw(nlohmann::detail::out_of_range::create(403,"enum value out of range",nullptr)); \ } From 95c29df7e6647712b3b21e5be30b97f605b57b5f Mon Sep 17 00:00:00 2001 From: Caillin Nugent Date: Wed, 13 May 2026 14:30:13 +1000 Subject: [PATCH 08/14] refactor(macros): make NLOHMANN_SERIALIZE_ENUM_STRICT use error code 410 - added error code 410 to docs Signed-off-by: Caillin Nugent --- docs/mkdocs/docs/home/exceptions.md | 10 ++++++++++ include/nlohmann/detail/macro_scope.hpp | 4 ++-- tests/src/unit-conversions.cpp | 4 ++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/docs/mkdocs/docs/home/exceptions.md b/docs/mkdocs/docs/home/exceptions.md index 30487348ee..4507c01a7a 100644 --- a/docs/mkdocs/docs/home/exceptions.md +++ b/docs/mkdocs/docs/home/exceptions.md @@ -868,6 +868,16 @@ Key identifiers to be serialized to BSON cannot contain code point U+0000, since BSON key cannot contain code point U+0000 (at byte 2) ``` +### json.exception.out_of_range.410 + +Undefined json fields cannot be used with JSON_SERIALIZE_ENUM_STRICT + +!!! failure "Example message" + + ``` + enum value out of range + ``` + ## Further exceptions This exception is thrown in case of errors that cannot be classified with the diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index edcb5b1163..a9eb9aa4fe 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -286,7 +286,7 @@ void templated_json_throw(ExceptionType exception) return ej_pair.first == e; \ }); \ if (it != std::end(m)) j = it->second; \ - else templated_json_throw(nlohmann::detail::out_of_range::create(403,"enum value out of range",nullptr)); \ + else templated_json_throw(nlohmann::detail::out_of_range::create(410,"enum value out of range",nullptr)); \ } \ template \ inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ @@ -301,7 +301,7 @@ void templated_json_throw(ExceptionType exception) return ej_pair.second == j; \ }); \ if (it != std::end(m)) e = it->first; \ - else templated_json_throw(nlohmann::detail::out_of_range::create(403,"enum value out of range",nullptr)); \ + else templated_json_throw(nlohmann::detail::out_of_range::create(410,"enum value out of range",nullptr)); \ } diff --git a/tests/src/unit-conversions.cpp b/tests/src/unit-conversions.cpp index a73102b7c3..563fa2c249 100644 --- a/tests/src/unit-conversions.cpp +++ b/tests/src/unit-conversions.cpp @@ -1704,7 +1704,7 @@ TEST_CASE("Strict JSON to enum mapping") // invalid json -> exception thrown json _; - CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.403] enum value out of range", json::out_of_range&); + CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.410] enum value out of range", json::out_of_range&); } SECTION("traditional enum") @@ -1723,7 +1723,7 @@ TEST_CASE("Strict JSON to enum mapping") // invalid json -> exception thrown json _; - CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.403] enum value out of range", json::out_of_range&); + CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.410] enum value out of range", json::out_of_range&); } } From 7217e982c2c2b2222a1a6011915e1feb8e6c64df Mon Sep 17 00:00:00 2001 From: Caillin Nugent Date: Wed, 13 May 2026 17:24:55 +1000 Subject: [PATCH 09/14] tests(macros): add test for to_json with enum value not mentioned in mapping for NLOHMANN_JSON_SERIALIZE_ENUM_STRICT Signed-off-by: Caillin Nugent --- include/nlohmann/detail/macro_scope.hpp | 10 +++++----- tests/src/unit-conversions.cpp | 9 ++++++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index a9eb9aa4fe..de50555d37 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -257,13 +257,13 @@ /*! @brief function to wrap JSON_THROW_MACRO - NLOHMANN_SERIALIZE_ENUM_STRICT has a - compilation warning about there being no arguments to JSON_THROW that depend on - template arguments otherwise + compilation warning about there being no arguments to JSON_THROW that depend on + template arguments otherwise */ template void templated_json_throw(ExceptionType exception) { - JSON_THROW(exception); + JSON_THROW(exception); } /*! @@ -286,7 +286,7 @@ void templated_json_throw(ExceptionType exception) return ej_pair.first == e; \ }); \ if (it != std::end(m)) j = it->second; \ - else templated_json_throw(nlohmann::detail::out_of_range::create(410,"enum value out of range",nullptr)); \ + else templated_json_throw(nlohmann::detail::out_of_range::create(410,"enum value out of range",nullptr)); \ } \ template \ inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ @@ -301,7 +301,7 @@ void templated_json_throw(ExceptionType exception) return ej_pair.second == j; \ }); \ if (it != std::end(m)) e = it->first; \ - else templated_json_throw(nlohmann::detail::out_of_range::create(410,"enum value out of range",nullptr)); \ + else templated_json_throw(nlohmann::detail::out_of_range::create(410,"enum value out of range",nullptr)); \ } diff --git a/tests/src/unit-conversions.cpp b/tests/src/unit-conversions.cpp index 563fa2c249..83c0cfce5c 100644 --- a/tests/src/unit-conversions.cpp +++ b/tests/src/unit-conversions.cpp @@ -1657,7 +1657,7 @@ TEST_CASE("JSON to enum mapping") } } -enum class strict_cards {kreuz, pik, herz, karo}; +enum class strict_cards {kreuz, pik, herz, karo, andere}; // andere not included in mapping // NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(strict_cards, @@ -1674,6 +1674,7 @@ enum StrictTaskState // NOLINT(cert-int09-c,readability-enum-initial-value,cppco STRICT_TS_STOPPED, STRICT_TS_RUNNING, STRICT_TS_COMPLETED, + STRICT_TS_OTHER, // STRICT_TS_OTHER not in mapping STRICT_TS_INVALID = -1, }; @@ -1705,6 +1706,9 @@ TEST_CASE("Strict JSON to enum mapping") // invalid json -> exception thrown json _; CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.410] enum value out of range", json::out_of_range&); + + // conversion of unmapped enum -> exception thrown + CHECK_THROWS_WITH_AS(json(strict_cards::andere), "[json.exception.out_of_range.410] enum value out of range", json::out_of_range&); } SECTION("traditional enum") @@ -1724,6 +1728,9 @@ TEST_CASE("Strict JSON to enum mapping") // invalid json -> exception thrown json _; CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.410] enum value out of range", json::out_of_range&); + + // conversion of unmapped enum -> exception thrown + CHECK_THROWS_WITH_AS(json(STRICT_TS_OTHER), "[json.exception.out_of_range.410] enum value out of range", json::out_of_range&); } } From d221941390d4db97bfd7011dfffb659b3b50f168 Mon Sep 17 00:00:00 2001 From: Caillin Nugent Date: Wed, 13 May 2026 17:29:34 +1000 Subject: [PATCH 10/14] Apply suggestions from code review Co-authored-by: Niels Lohmann Signed-off-by: Caillin Nugent --- docs/mkdocs/docs/features/enum_conversion.md | 2 +- include/nlohmann/detail/macro_scope.hpp | 1 - tests/src/unit-conversions.cpp | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/mkdocs/docs/features/enum_conversion.md b/docs/mkdocs/docs/features/enum_conversion.md index 89675c2795..d75d6e1121 100644 --- a/docs/mkdocs/docs/features/enum_conversion.md +++ b/docs/mkdocs/docs/features/enum_conversion.md @@ -56,7 +56,7 @@ Other Important points: - When using `get()`, undefined JSON values will default to the first pair specified in your map. Select this default pair carefully. If you desire an exception in this circumstance use [`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT()`](../api/macros/nlohmann_json_serialize_enum_strict.md) - which behaves identically except for throwing an exception on unrecognised values. + which behaves identically except for throwing an exception on unrecognized values. - 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). diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index de50555d37..9f909e21b2 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -304,7 +304,6 @@ void templated_json_throw(ExceptionType exception) else templated_json_throw(nlohmann::detail::out_of_range::create(410,"enum value out of range",nullptr)); \ } - // 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 83c0cfce5c..1029fb8f6f 100644 --- a/tests/src/unit-conversions.cpp +++ b/tests/src/unit-conversions.cpp @@ -1664,7 +1664,7 @@ NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(strict_cards, { {strict_cards::kreuz, "kreuz"}, {strict_cards::pik, "pik"}, - {strict_cards::pik, "puk"}, // second entry for cards::puk; will not be used + {strict_cards::pik, "puk"}, // second entry for cards::pik; will not be used {strict_cards::herz, "herz"}, {strict_cards::karo, "karo"} }) From 21fe58fa43745a846c6987f772094a6a0a4736c0 Mon Sep 17 00:00:00 2001 From: Caillin Nugent Date: Wed, 13 May 2026 18:50:57 +1000 Subject: [PATCH 11/14] fix(macro): prevent compilation error with -Werror and -Wunused-parameter with NLOHMANN_JSON_SERIALIZE_ENUM_STRICT - casted exception to void to avoid warning Signed-off-by: Caillin Nugent --- include/nlohmann/detail/macro_scope.hpp | 4 ++++ single_include/nlohmann/json.hpp | 22 +++++++++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 9f909e21b2..b9ff5e016d 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -264,6 +264,10 @@ template void templated_json_throw(ExceptionType exception) { JSON_THROW(exception); + + /* JSON_THROW(exception) discards exception and aborts - void cast needed to supress + compilation error if compiled with -Werror and Wunused-parameter */ + (void)exception; } /*! diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 53fd416288..4f25b7e5a2 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2617,6 +2617,23 @@ JSON_HEDLEY_DIAGNOSTIC_POP e = ((it != std::end(m)) ? it : std::begin(m))->first; \ } + + +/*! +@brief function to wrap JSON_THROW_MACRO - NLOHMANN_SERIALIZE_ENUM_STRICT has a + compilation warning about there being no arguments to JSON_THROW that depend on + template arguments otherwise +*/ +template +void templated_json_throw(ExceptionType exception) +{ + JSON_THROW(exception); + + /* JSON_THROW(exception) discards exception and aborts - void cast needed to supress + compilation error if compiled with -Werror and Wunused-parameter */ + (void)exception; +} + /*! @brief macro to briefly define a mapping between an enum and JSON with exception on invalid input @@ -2637,7 +2654,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP return ej_pair.first == e; \ }); \ if (it != std::end(m)) j = it->second; \ - else throw nlohmann::detail::out_of_range::create(403, "enum value out of range", nullptr);\ + else templated_json_throw(nlohmann::detail::out_of_range::create(410,"enum value out of range",nullptr)); \ } \ template \ inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ @@ -2652,10 +2669,9 @@ JSON_HEDLEY_DIAGNOSTIC_POP return ej_pair.second == j; \ }); \ if (it != std::end(m)) e = it->first; \ - else throw nlohmann::detail::out_of_range::create(403, "enum value out of range", nullptr);\ + else templated_json_throw(nlohmann::detail::out_of_range::create(410,"enum value out of range",nullptr)); \ } - // Ugly macros to avoid uglier copy-paste when specializing basic_json. They // may be removed in the future once the class is split. From 8daaab0e21ca9d2827f0a0fbba1ed964b7efb018 Mon Sep 17 00:00:00 2001 From: Caillin Nugent Date: Fri, 15 May 2026 11:51:15 +1000 Subject: [PATCH 12/14] fix(docs): add link to NLOHMANN_SERIALIZE_ENUM_STRICT docs to exception page Signed-off-by: Caillin Nugent --- docs/mkdocs/docs/home/exceptions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/mkdocs/docs/home/exceptions.md b/docs/mkdocs/docs/home/exceptions.md index 4507c01a7a..472cecda36 100644 --- a/docs/mkdocs/docs/home/exceptions.md +++ b/docs/mkdocs/docs/home/exceptions.md @@ -870,7 +870,7 @@ Key identifiers to be serialized to BSON cannot contain code point U+0000, since ### json.exception.out_of_range.410 -Undefined json fields cannot be used with JSON_SERIALIZE_ENUM_STRICT +Undefined json fields cannot be used with [`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT`](../api/macros/nlohmann_json_serialize_enum_strict.md) !!! failure "Example message" From 8f6701b13802c027e78986b4d3e51b0e6078b40f Mon Sep 17 00:00:00 2001 From: Caillin Nugent Date: Fri, 15 May 2026 12:38:47 +1000 Subject: [PATCH 13/14] docs(macros): add example of exception throwing for NLOHMANN_JSON_SERIALIZE_ENUM_STRICT Signed-off-by: Caillin Nugent --- .../nlohmann_json_serialize_enum_strict.md | 16 ++++++ ...lohmann_json_serialize_enum_strict_err.cpp | 53 +++++++++++++++++++ ...mann_json_serialize_enum_strict_err.output | 2 + 3 files changed, 71 insertions(+) create mode 100644 docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_err.cpp create mode 100644 docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_err.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 fed2171ff8..f4c52e0706 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 @@ -75,6 +75,22 @@ inline void from_json(const BasicJsonType& j, type& e); --8<-- "examples/nlohmann_json_serialize_enum_strict_2.output" ``` +??? example "Example 3: exceptions on invalid serialization" + + The example shows how an invalid serialization causes an exception to be thrown. In the example, + Color::unknown is not defined in the mapping used to call `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT` + so causes an exception when used to serialize. Similarly, "what" does not refer to an enum + value so also causes an exception when deserialization is attempted. + + ```cpp hl_lines="14 32 33 43 44 45" + --8<-- "examples/nlohmann_json_serialize_enum_strict_err.cpp" + ``` + + Output: + ```json + --8<-- "examples/nlohmann_json_serialize_enum_strict_err.output" + ``` + ## See also - [Specializing enum conversion](../../features/enum_conversion.md) diff --git a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_err.cpp b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_err.cpp new file mode 100644 index 0000000000..aeb9011228 --- /dev/null +++ b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_err.cpp @@ -0,0 +1,53 @@ +#include +#include + +using json = nlohmann::json; + +namespace ns +{ + +enum class Color +{ + red, + green, + blue, + unknown // not mapped in JSON_SERIALIZE_ENUM_STRICT +}; + +NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(Color, +{ + {Color::red, "red"}, + {Color::green, "green"}, + {Color::blue, "blue"} +}) + +} // namespace ns + + +int main() +{ + // invalid serialization + try + { + // ns::color::unknown was not mapped in macro + json invalid_serialization = ns::Color::unknown; + } + catch (const json::exception e) + { + std::cout << "deserialization failed: " << e.what() << std::endl; + } + + // invalid deserialization + try + { + // what does not map to an enum + json invalid_deserialization("what"); + ns::Color color = invalid_deserialization.get(); + } + catch (const json::exception e) + { + std::cout << "deserialization failed: " << e.what() << std::endl; + } + + return 0; +} diff --git a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_err.output b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_err.output new file mode 100644 index 0000000000..62dd0a41ea --- /dev/null +++ b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_err.output @@ -0,0 +1,2 @@ +deserialization failed: [json.exception.out_of_range.410] enum value out of range +deserialization failed: [json.exception.out_of_range.410] enum value out of range From 3915dd0f7e8d36d4d556e25d7419ec806ecc327a Mon Sep 17 00:00:00 2001 From: Caillin Nugent Date: Fri, 15 May 2026 13:04:23 +1000 Subject: [PATCH 14/14] refactor(macros): add more in-depth error message to NLOHMANN_JSON_SERIALIZE_ENUM_STRICT - changed error message to follow style of nlohmann/json#4989 - made description of throw wrapper more general - updated tests and example of exceptions Signed-off-by: Caillin Nugent --- .../nlohmann_json_serialize_enum_strict_err.output | 4 ++-- include/nlohmann/detail/macro_scope.hpp | 10 +++++----- single_include/nlohmann/json.hpp | 10 +++++----- tests/src/unit-conversions.cpp | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_err.output b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_err.output index 62dd0a41ea..4894b4c7d2 100644 --- a/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_err.output +++ b/docs/mkdocs/docs/examples/nlohmann_json_serialize_enum_strict_err.output @@ -1,2 +1,2 @@ -deserialization failed: [json.exception.out_of_range.410] enum value out of range -deserialization failed: [json.exception.out_of_range.410] enum value out of range +deserialization failed: [json.exception.out_of_range.410] enum value out of range for Color +deserialization failed: [json.exception.out_of_range.410] enum value out of range for Color: "what" diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index b9ff5e016d..24fa4e5efb 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -256,9 +256,9 @@ /*! -@brief function to wrap JSON_THROW_MACRO - NLOHMANN_SERIALIZE_ENUM_STRICT has a - compilation warning about there being no arguments to JSON_THROW that depend on - template arguments otherwise +@brief function to wrap JSON_THROW_MACRO - there can be compilation errors about + there being no arguments to JSON_THROW that depend on template arguments + if this is not used to call JSON_THROW */ template void templated_json_throw(ExceptionType exception) @@ -290,7 +290,7 @@ void templated_json_throw(ExceptionType exception) return ej_pair.first == e; \ }); \ if (it != std::end(m)) j = it->second; \ - else templated_json_throw(nlohmann::detail::out_of_range::create(410,"enum value out of range",nullptr)); \ + else templated_json_throw(nlohmann::detail::out_of_range::create(410,"enum value out of range for " #ENUM_TYPE, nullptr)); \ } \ template \ inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ @@ -305,7 +305,7 @@ void templated_json_throw(ExceptionType exception) return ej_pair.second == j; \ }); \ if (it != std::end(m)) e = it->first; \ - else templated_json_throw(nlohmann::detail::out_of_range::create(410,"enum value out of range",nullptr)); \ + else templated_json_throw(nlohmann::detail::out_of_range::create(410,"enum value out of range for " #ENUM_TYPE ": " + j.dump(), &j)); \ } // Ugly macros to avoid uglier copy-paste when specializing basic_json. They diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 4f25b7e5a2..22d3106204 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2620,9 +2620,9 @@ JSON_HEDLEY_DIAGNOSTIC_POP /*! -@brief function to wrap JSON_THROW_MACRO - NLOHMANN_SERIALIZE_ENUM_STRICT has a - compilation warning about there being no arguments to JSON_THROW that depend on - template arguments otherwise +@brief function to wrap JSON_THROW_MACRO - there can be compilation errors about + there being no arguments to JSON_THROW that depend on template arguments + if this is not used to call JSON_THROW */ template void templated_json_throw(ExceptionType exception) @@ -2654,7 +2654,7 @@ void templated_json_throw(ExceptionType exception) return ej_pair.first == e; \ }); \ if (it != std::end(m)) j = it->second; \ - else templated_json_throw(nlohmann::detail::out_of_range::create(410,"enum value out of range",nullptr)); \ + else templated_json_throw(nlohmann::detail::out_of_range::create(410,"enum value out of range for " #ENUM_TYPE, nullptr)); \ } \ template \ inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ @@ -2669,7 +2669,7 @@ void templated_json_throw(ExceptionType exception) return ej_pair.second == j; \ }); \ if (it != std::end(m)) e = it->first; \ - else templated_json_throw(nlohmann::detail::out_of_range::create(410,"enum value out of range",nullptr)); \ + else templated_json_throw(nlohmann::detail::out_of_range::create(410,"enum value out of range for " #ENUM_TYPE ": " + j.dump(), &j)); \ } // Ugly macros to avoid uglier copy-paste when specializing basic_json. They diff --git a/tests/src/unit-conversions.cpp b/tests/src/unit-conversions.cpp index 1029fb8f6f..4581fa660e 100644 --- a/tests/src/unit-conversions.cpp +++ b/tests/src/unit-conversions.cpp @@ -1705,10 +1705,10 @@ TEST_CASE("Strict JSON to enum mapping") // invalid json -> exception thrown json _; - CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.410] enum value out of range", json::out_of_range&); + CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.410] enum value out of range for strict_cards: \"what?\"", json::out_of_range&); // conversion of unmapped enum -> exception thrown - CHECK_THROWS_WITH_AS(json(strict_cards::andere), "[json.exception.out_of_range.410] enum value out of range", json::out_of_range&); + CHECK_THROWS_WITH_AS(json(strict_cards::andere), "[json.exception.out_of_range.410] enum value out of range for strict_cards", json::out_of_range&); } SECTION("traditional enum") @@ -1727,10 +1727,10 @@ TEST_CASE("Strict JSON to enum mapping") // invalid json -> exception thrown json _; - CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.410] enum value out of range", json::out_of_range&); + CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.410] enum value out of range for StrictTaskState: \"what?\"", json::out_of_range&); // conversion of unmapped enum -> exception thrown - CHECK_THROWS_WITH_AS(json(STRICT_TS_OTHER), "[json.exception.out_of_range.410] enum value out of range", json::out_of_range&); + CHECK_THROWS_WITH_AS(json(STRICT_TS_OTHER), "[json.exception.out_of_range.410] enum value out of range for StrictTaskState", json::out_of_range&); } }