mirror of
https://github.com/nlohmann/json.git
synced 2026-10-03 21:20:30 +00:00
Fix NLOHMANN_JSON_SERIALIZE_ENUM_STRICT's from_json message (#5698)
from_json built its out_of_range.410 message with "..." + j.dump(). If the unmatched value is (or contains) a string with invalid UTF-8, that dump() itself throws type_error.316, so the caller got type_error.316 instead of the documented out_of_range.410; such strings can reach get<Enum>() unvalidated, e.g. from from_cbor()/from_msgpack(). With a custom string_t, j.dump() returns that type, and "const char*" + string_t does not compile unless the type happens to provide operator+, so the macro failed to compile for such types. Build the message with detail::concat(), which appends any type exposing data()/size() and always yields a std::string, and dump with error_handler_t::replace so building the message itself cannot throw. Added regression tests: an invalid-UTF-8 case in the existing strict-enum test in unit-conversions.cpp, and a strict-enum use with alt_string (the custom string_t from unit-alt-string.cpp) to cover the compile failure. Fixes #5667. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
@@ -336,7 +336,7 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
return ej_pair.second == j; \
|
return ej_pair.second == j; \
|
||||||
}); \
|
}); \
|
||||||
if (it != std::end(m)) e = it->first; \
|
if (it != std::end(m)) e = it->first; \
|
||||||
else templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(410,"enum value out of range for " #ENUM_TYPE ": " + j.dump(), &j)); \
|
else templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(410, nlohmann::detail::concat("enum value out of range for " #ENUM_TYPE ": ", j.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace)), &j)); \
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
|
// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
|
||||||
|
|||||||
@@ -2749,7 +2749,7 @@ void templated_json_throw(ExceptionType exception)
|
|||||||
return ej_pair.second == j; \
|
return ej_pair.second == j; \
|
||||||
}); \
|
}); \
|
||||||
if (it != std::end(m)) e = it->first; \
|
if (it != std::end(m)) e = it->first; \
|
||||||
else templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(410,"enum value out of range for " #ENUM_TYPE ": " + j.dump(), &j)); \
|
else templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(410, nlohmann::detail::concat("enum value out of range for " #ENUM_TYPE ": ", j.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace)), &j)); \
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
|
// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
|
||||||
|
|||||||
@@ -174,6 +174,15 @@ bool operator<(const char* op1, const alt_string& op2) noexcept
|
|||||||
return op1 < op2.str_impl;
|
return op1 < op2.str_impl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class alt_color { red, green };
|
||||||
|
|
||||||
|
// 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(alt_color,
|
||||||
|
{
|
||||||
|
{alt_color::red, "red"},
|
||||||
|
{alt_color::green, "green"},
|
||||||
|
})
|
||||||
|
|
||||||
TEST_CASE("alternative string type")
|
TEST_CASE("alternative string type")
|
||||||
{
|
{
|
||||||
SECTION("binary formats")
|
SECTION("binary formats")
|
||||||
@@ -374,4 +383,19 @@ TEST_CASE("alternative string type")
|
|||||||
const auto j2 = j.flatten();
|
const auto j2 = j.flatten();
|
||||||
CHECK(j2.dump() == R"({"/foo/0":"bar","/foo/1":"baz"})");
|
CHECK(j2.dump() == R"({"/foo/0":"bar","/foo/1":"baz"})");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("strict enum")
|
||||||
|
{
|
||||||
|
// regression test for #5667: NLOHMANN_JSON_SERIALIZE_ENUM_STRICT's from_json
|
||||||
|
// built its exception message with "..." + j.dump(), which does not compile
|
||||||
|
// when j.dump() returns a custom string_t (here alt_string) instead of
|
||||||
|
// std::string
|
||||||
|
alt_json doc;
|
||||||
|
doc = "red";
|
||||||
|
CHECK(doc.get<alt_color>() == alt_color::red);
|
||||||
|
|
||||||
|
alt_json _;
|
||||||
|
doc = "blue";
|
||||||
|
CHECK_THROWS_WITH_AS(_ = doc.get<alt_color>(), "[json.exception.out_of_range.410] enum value out of range for alt_color: \"blue\"", alt_json::out_of_range&);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1748,6 +1748,12 @@ TEST_CASE("Strict JSON to enum mapping")
|
|||||||
|
|
||||||
// conversion of unmapped enum -> exception thrown
|
// 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 for strict_cards", 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&);
|
||||||
|
|
||||||
|
// invalid UTF-8 -> out_of_range.410, not the type_error.316 thrown while building the
|
||||||
|
// message (regression test for #5667); such strings can reach get<Enum>() unvalidated,
|
||||||
|
// e.g. from from_cbor()/from_msgpack() (#5529)
|
||||||
|
const json j_invalid_utf8 = "\xFF";
|
||||||
|
CHECK_THROWS_WITH_AS(_ = j_invalid_utf8.get<strict_cards>(), "[json.exception.out_of_range.410] enum value out of range for strict_cards: \"\xEF\xBF\xBD\"", json::out_of_range&);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("traditional enum")
|
SECTION("traditional enum")
|
||||||
|
|||||||
Reference in New Issue
Block a user