mirror of
https://github.com/nlohmann/json.git
synced 2026-09-30 03:30:31 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c512947c01 |
@@ -96,8 +96,5 @@ Linear.
|
||||
|
||||
## Version history
|
||||
|
||||
1. Added in version 3.11.0. Fixed in version 3.13.0 to keep the result independent of nesting depth: before, a pair
|
||||
of binary values with the same bytes but a different subtype - unequal by `operator==`, yet equivalent by
|
||||
`operator<=>` - could end an ordered comparison as `unordered` only past 128 levels of nesting, or at every depth
|
||||
with [`JSON_NO_THREAD_LOCAL`](../macros/json_no_thread_local.md) defined.
|
||||
1. Added in version 3.11.0.
|
||||
2. Added in version 3.11.0.
|
||||
|
||||
@@ -336,7 +336,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>(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
|
||||
|
||||
@@ -1307,65 +1307,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
*/
|
||||
template<bool Ordered>
|
||||
static compare_result compare_leaves(const_reference lhs, const_reference rhs) noexcept
|
||||
{
|
||||
return compare_leaves(lhs, rhs, std::integral_constant<bool, Ordered> {});
|
||||
}
|
||||
|
||||
/// @brief compare two leaves that are only being checked for equality
|
||||
static compare_result compare_leaves(const_reference lhs, const_reference rhs, std::false_type /*ordered*/) noexcept
|
||||
{
|
||||
if (lhs == rhs)
|
||||
{
|
||||
return compare_result::equal;
|
||||
}
|
||||
|
||||
return order_leaves(lhs, rhs, std::false_type {});
|
||||
return order_leaves(lhs, rhs, std::integral_constant<bool, Ordered> {});
|
||||
}
|
||||
|
||||
#if JSON_HAS_THREE_WAY_COMPARISON
|
||||
/*!
|
||||
@brief compare two leaves that are being ordered, for operator<=>
|
||||
|
||||
Reached only from operator<=>, so the leaves must be classified exactly
|
||||
as operator<=> classifies them - which is not the same as asking
|
||||
== and then order_leaves(), the way the other overload does it. The two
|
||||
disagree on a binary value: == also compares the subtype, but <=> compares
|
||||
only the bytes, through std::vector<std::uint8_t>::operator<=>. Using <=>
|
||||
itself here keeps a leaf pair classified the same way regardless of how
|
||||
deep it is nested - == first would again call operator<=> a level down
|
||||
through order_leaves(), but call it after a mismatching == already ended
|
||||
the comparison for a pair that <=> alone would still call equivalent.
|
||||
*/
|
||||
static compare_result compare_leaves(const_reference lhs, const_reference rhs, std::true_type /*ordered*/) noexcept
|
||||
{
|
||||
const std::partial_ordering order = lhs <=> rhs; // *NOPAD*
|
||||
if (order == 0)
|
||||
{
|
||||
return compare_result::equal;
|
||||
}
|
||||
if (order < 0)
|
||||
{
|
||||
return compare_result::less;
|
||||
}
|
||||
if (order > 0)
|
||||
{
|
||||
return compare_result::greater;
|
||||
}
|
||||
return compare_result::unordered;
|
||||
}
|
||||
#else
|
||||
/// @brief compare two leaves that are being ordered, for operator<
|
||||
static compare_result compare_leaves(const_reference lhs, const_reference rhs, std::true_type /*ordered*/) noexcept
|
||||
{
|
||||
if (lhs == rhs)
|
||||
{
|
||||
return compare_result::equal;
|
||||
}
|
||||
|
||||
return order_leaves(lhs, rhs, std::true_type {});
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
@brief compare two object keys
|
||||
|
||||
|
||||
@@ -2747,7 +2747,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>(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
|
||||
@@ -27388,65 +27388,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
*/
|
||||
template<bool Ordered>
|
||||
static compare_result compare_leaves(const_reference lhs, const_reference rhs) noexcept
|
||||
{
|
||||
return compare_leaves(lhs, rhs, std::integral_constant<bool, Ordered> {});
|
||||
}
|
||||
|
||||
/// @brief compare two leaves that are only being checked for equality
|
||||
static compare_result compare_leaves(const_reference lhs, const_reference rhs, std::false_type /*ordered*/) noexcept
|
||||
{
|
||||
if (lhs == rhs)
|
||||
{
|
||||
return compare_result::equal;
|
||||
}
|
||||
|
||||
return order_leaves(lhs, rhs, std::false_type {});
|
||||
return order_leaves(lhs, rhs, std::integral_constant<bool, Ordered> {});
|
||||
}
|
||||
|
||||
#if JSON_HAS_THREE_WAY_COMPARISON
|
||||
/*!
|
||||
@brief compare two leaves that are being ordered, for operator<=>
|
||||
|
||||
Reached only from operator<=>, so the leaves must be classified exactly
|
||||
as operator<=> classifies them - which is not the same as asking
|
||||
== and then order_leaves(), the way the other overload does it. The two
|
||||
disagree on a binary value: == also compares the subtype, but <=> compares
|
||||
only the bytes, through std::vector<std::uint8_t>::operator<=>. Using <=>
|
||||
itself here keeps a leaf pair classified the same way regardless of how
|
||||
deep it is nested - == first would again call operator<=> a level down
|
||||
through order_leaves(), but call it after a mismatching == already ended
|
||||
the comparison for a pair that <=> alone would still call equivalent.
|
||||
*/
|
||||
static compare_result compare_leaves(const_reference lhs, const_reference rhs, std::true_type /*ordered*/) noexcept
|
||||
{
|
||||
const std::partial_ordering order = lhs <=> rhs; // *NOPAD*
|
||||
if (order == 0)
|
||||
{
|
||||
return compare_result::equal;
|
||||
}
|
||||
if (order < 0)
|
||||
{
|
||||
return compare_result::less;
|
||||
}
|
||||
if (order > 0)
|
||||
{
|
||||
return compare_result::greater;
|
||||
}
|
||||
return compare_result::unordered;
|
||||
}
|
||||
#else
|
||||
/// @brief compare two leaves that are being ordered, for operator<
|
||||
static compare_result compare_leaves(const_reference lhs, const_reference rhs, std::true_type /*ordered*/) noexcept
|
||||
{
|
||||
if (lhs == rhs)
|
||||
{
|
||||
return compare_result::equal;
|
||||
}
|
||||
|
||||
return order_leaves(lhs, rhs, std::true_type {});
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
@brief compare two object keys
|
||||
|
||||
|
||||
@@ -174,6 +174,15 @@ bool operator<(const char* op1, const alt_string& op2) noexcept
|
||||
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")
|
||||
{
|
||||
SECTION("binary formats")
|
||||
@@ -374,4 +383,19 @@ TEST_CASE("alternative string type")
|
||||
const auto j2 = j.flatten();
|
||||
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&);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -952,51 +952,3 @@ TEST_CASE("containers are compared element by element")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if JSON_HAS_THREE_WAY_COMPARISON
|
||||
// JSON_HAS_CPP_20 (do not remove; see note at top of file)
|
||||
TEST_CASE("operator<=> of binary values with a different subtype does not depend on nesting depth")
|
||||
{
|
||||
// #5654: std::vector<std::uint8_t>::operator<=>, which the binary type's
|
||||
// own operator<=> uses, ignores the subtype that operator== checks. So a
|
||||
// pair of binary values with the same bytes but a different subtype is
|
||||
// unequal, yet <=>-equivalent - the same inconsistency between == and <=>
|
||||
// that a NaN has. Within the nesting bound, an array compares itself
|
||||
// with std::vector's own operator<=>, which treats an equivalent pair as
|
||||
// undecided and lets the next element decide, same as
|
||||
// std::lexicographical_compare_three_way does. Past the bound,
|
||||
// compare_iteratively<true>() takes over and must classify the pair the
|
||||
// same way, or the result of operator<=> - and of <, which C++20 derives
|
||||
// from it - depends on how deeply the values are nested.
|
||||
const json a = json::array({json::binary({1}, 1), 1});
|
||||
const json b = json::array({json::binary({1}, 2), 2});
|
||||
|
||||
// the root inconsistency: unequal, yet <=>-equivalent
|
||||
CHECK_FALSE(a[0] == b[0]);
|
||||
CHECK((a[0] <=> b[0]) == std::partial_ordering::equivalent); // *NOPAD*
|
||||
|
||||
const auto deep = [](const json & j, const std::size_t depth)
|
||||
{
|
||||
json result = j;
|
||||
for (std::size_t i = 0; i < depth; ++i)
|
||||
{
|
||||
result = json::array({std::move(result)});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
// 127 levels stay within nesting_depth_limit() (128); 128 and 200 do not,
|
||||
// and must still agree with the levels that do
|
||||
for (const std::size_t depth : std::vector<std::size_t> {0, 127, 128, 200})
|
||||
{
|
||||
CAPTURE(depth);
|
||||
const json x = deep(a, depth);
|
||||
const json y = deep(b, depth);
|
||||
CHECK((x <=> y) == std::partial_ordering::less); // *NOPAD*
|
||||
CHECK((y <=> x) == std::partial_ordering::greater); // *NOPAD*
|
||||
CHECK(x < y);
|
||||
CHECK(y > x);
|
||||
CHECK_FALSE(y < x);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1740,6 +1740,12 @@ TEST_CASE("Strict JSON to enum mapping")
|
||||
|
||||
// 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&);
|
||||
|
||||
// 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")
|
||||
|
||||
Reference in New Issue
Block a user