Compare commits

..
Author SHA1 Message Date
Niels Lohmann 666801a2c6 Fix key types convertible to std::string_view breaking lookups
Since #4958, a key type implicitly convertible to std::string_view was
accepted by is_usable_as_basic_json_key_type without checking that the
object's comparator can actually compare object_t::key_type with that
key type. The key was then forwarded unchanged to the underlying map,
so const operator[], at, find, count, contains, erase and value failed
to compile (a hard error inside <map>) for a key convertible only to
std::string_view, and value() rejected such keys outright. For keys
convertible to both std::string and std::string_view, the KeyType&&
templates now won overload resolution over the object_t::key_type
overloads and then failed the same way, a regression from 3.12.0. Only
the non-const operator[] worked, because it uses emplace(), which
constructs a std::string from the key explicitly. ordered_json was not
affected, since ordered_map checks comparability itself.

Add a trait, is_string_view_convertible_key_type, that recognizes a key
type that is convertible to std::string_view but not directly
comparable with the object's key type, provided std::string_view itself
is comparable with it. at(), operator[], find(), count(), contains(),
erase() and value() now route such keys through a new lookup_key()
helper that converts them to std::string_view before they reach the
object, matching how the object's transparent comparator already
supports std::string_view lookups.

Fixes #5663.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 23:31:37 +02:00
11 changed files with 245 additions and 57 deletions
+3 -1
View File
@@ -224,5 +224,7 @@ Strong exception safety: if an exception occurs, the original value stays intact
1. Added in version 1.0.0.
2. Added in version 1.0.0.
3. Added in version 3.11.0.
3. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as
already supported by [`operator[]`](operator[].md), [`value`](value.md), [`find`](find.md), and other lookup
functions.
4. Added in version 2.0.0.
+3 -1
View File
@@ -115,5 +115,7 @@ Logarithmic in the size of the JSON object.
## Version history
1. Added in version 3.11.0.
2. Added in version 3.6.0. Extended template `KeyType` to support comparable types in version 3.11.0.
2. Added in version 3.6.0. Extended template `KeyType` to support comparable types in version 3.11.0. Fixed in
version 3.13.0 to consistently accept `std::string_view`-convertible keys, as already supported by
[`operator[]`](operator[].md), [`at`](at.md), [`value`](value.md), and other lookup functions.
3. Added in version 3.7.0.
+3 -1
View File
@@ -80,4 +80,6 @@ This method always returns `0` when executed on a JSON type that is not an objec
## Version history
1. Added in version 3.11.0.
2. Added in version 1.0.0. Changed parameter `key` type to `KeyType&&` in version 3.11.0.
2. Added in version 1.0.0. Changed parameter `key` type to `KeyType&&` in version 3.11.0. Fixed in version 3.13.0 to
consistently accept `std::string_view`-convertible keys, as already supported by [`operator[]`](operator[].md),
[`at`](at.md), [`value`](value.md), and other lookup functions.
+3 -1
View File
@@ -213,5 +213,7 @@ Strong exception safety: if an exception occurs, the original value stays intact
1. Added in version 1.0.0. Added support for binary types in version 3.8.0.
2. Added in version 1.0.0. Added support for binary types in version 3.8.0.
3. Added in version 1.0.0.
4. Added in version 3.11.0.
4. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as
already supported by [`operator[]`](operator[].md), [`at`](at.md), [`value`](value.md), and other lookup
functions.
5. Added in version 1.0.0.
+3 -1
View File
@@ -84,4 +84,6 @@ This method always returns `end()` when executed on a JSON type that is not an o
## Version history
1. Added in version 3.11.0.
2. Added in version 1.0.0. Changed to support comparable types in version 3.11.0.
2. Added in version 1.0.0. Changed to support comparable types in version 3.11.0. Fixed in version 3.13.0 to
consistently accept `std::string_view`-convertible keys, as already supported by [`operator[]`](operator[].md),
[`at`](at.md), [`value`](value.md), and other lookup functions.
+3 -1
View File
@@ -185,7 +185,9 @@ changes to any JSON value.
## Version history
1. Added in version 1.0.0. Changed parameter `default_value` type from `const ValueType&` to `ValueType&&` in version 3.11.0.
2. Added in version 3.11.0. Made `ValueType` the first template parameter in version 3.11.2.
2. Added in version 3.11.0. Made `ValueType` the first template parameter in version 3.11.2. Fixed in version 3.13.0
to consistently accept `std::string_view`-convertible keys, as already supported by
[`operator[]`](operator[].md), [`at`](at.md), [`find`](find.md), and other lookup functions.
3. Added in version 2.0.2. Extended to work with arrays in version 3.13.0, including fixing an issue where resolving
`ptr` through an array unexpectedly threw `out_of_range` instead of returning the resolved element (or
`default_value`, as documented).
+26 -4
View File
@@ -725,7 +725,7 @@ std::is_constructible <decltype(std::declval<Compare>()(std::declval<A>(), std::
// avoid their instantiation on all compilers, even when the first operand
// is false. The dispatch on is_json_pointer_of can be removed once the
// deprecated json_pointer comparison operators have been removed.
template<typename Compare, typename A, typename B, bool = is_json_pointer_of<uncvref_t<A>, uncvref_t<B>>::value>
template<typename Compare, typename A, typename B, bool = is_json_pointer_of<A, B>::value>
struct is_comparable : std::false_type {};
template<typename Compare, typename A, typename B>
@@ -748,6 +748,30 @@ using is_usable_as_key_type = typename std::conditional <
std::true_type,
std::false_type >::type;
#ifdef JSON_HAS_CPP_17
// type trait to check if KeyType can only be used as an object key after
// converting it to std::string_view: it is convertible to std::string_view, the
// object's comparator cannot compare it with object_t::key_type directly, but
// can compare a std::string_view. JSON pointers and JSON iterators are ruled out
// first, so that the conversion checks are never instantiated for them (a JSON
// pointer's deprecated conversion to string_t would be named otherwise).
template < typename BasicJsonType, typename KeyTypeCVRef, typename KeyType = uncvref_t<KeyTypeCVRef>,
bool = is_json_pointer<KeyType>::value || is_json_iterator_of<BasicJsonType, KeyType>::value >
struct is_string_view_convertible_key_type : std::false_type {};
template<typename BasicJsonType, typename KeyTypeCVRef, typename KeyType>
struct is_string_view_convertible_key_type<BasicJsonType, KeyTypeCVRef, KeyType, false>
: std::integral_constant < bool,
std::is_convertible<KeyTypeCVRef, std::string_view>::value
&& !is_usable_as_key_type<typename BasicJsonType::object_comparator_t,
typename BasicJsonType::object_t::key_type, KeyTypeCVRef, true, false>::value
&& is_usable_as_key_type<typename BasicJsonType::object_comparator_t,
typename BasicJsonType::object_t::key_type, std::string_view, true, false>::value > {};
#else
template<typename BasicJsonType, typename KeyTypeCVRef>
struct is_string_view_convertible_key_type : std::false_type {};
#endif
// type trait to check if KeyType can be used as an object key
// true if:
// - KeyType is comparable with BasicJsonType::object_t::key_type
@@ -761,9 +785,7 @@ using is_usable_as_basic_json_key_type = typename std::conditional <
typename BasicJsonType::object_t::key_type, KeyTypeCVRef,
RequireTransparentComparator, ExcludeObjectKeyType>::value
&& !is_json_iterator_of<BasicJsonType, KeyType>::value)
#ifdef JSON_HAS_CPP_17
|| std::is_convertible<KeyType, std::string_view>::value
#endif
|| is_string_view_convertible_key_type<BasicJsonType, KeyTypeCVRef>::value
, std::true_type,
std::false_type >::type;
+32 -12
View File
@@ -805,6 +805,24 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return it;
}
/// @brief the key to look up an object member with: the key itself, or its
/// std::string_view if the object can only be searched with that
template < typename KeyType, detail::enable_if_t <
!detail::is_string_view_convertible_key_type<basic_json_t, KeyType>::value, int > = 0 >
static KeyType && lookup_key(KeyType && key) noexcept
{
return std::forward<KeyType>(key);
}
#ifdef JSON_HAS_CPP_17
template < typename KeyType, detail::enable_if_t <
detail::is_string_view_convertible_key_type<basic_json_t, KeyType>::value, int > = 0 >
static std::string_view lookup_key(KeyType && key)
{
return std::forward<KeyType>(key);
}
#endif
/// @brief erase an element from the object and return the following one
/// Not every map returns an iterator from erase(iterator): some containers
/// (e.g., Abseil's hash maps) return void to avoid computing a successor
@@ -2766,7 +2784,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
}
auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
auto it = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
if (it == m_data.m_value.object->end())
{
JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this));
@@ -2804,7 +2822,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
}
auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
auto it = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
if (it == m_data.m_value.object->end())
{
JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this));
@@ -2940,7 +2958,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// operator[] only works for objects
if (JSON_HEDLEY_LIKELY(is_object()))
{
auto result = m_data.m_value.object->emplace(std::forward<KeyType>(key), nullptr);
auto result = m_data.m_value.object->emplace(lookup_key(std::forward<KeyType>(key)), nullptr);
return set_parent(result.first->second);
}
@@ -2956,7 +2974,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// const operator[] only works for objects
if (JSON_HEDLEY_LIKELY(is_object()))
{
auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
auto it = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
JSON_ASSERT(it != m_data.m_value.object->end());
return it->second;
}
@@ -2966,8 +2984,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
private:
template<typename KeyType>
using is_comparable_with_object_key = detail::is_comparable <
object_comparator_t, const typename object_t::key_type&, KeyType >;
using is_comparable_with_object_key = std::integral_constant < bool,
detail::is_comparable <
object_comparator_t, const typename object_t::key_type&, KeyType >::value
|| detail::is_string_view_convertible_key_type<basic_json_t, KeyType>::value >;
template<typename ValueType>
using value_return_type = std::conditional <
@@ -3350,7 +3370,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
}
const auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
const auto it = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
if (it != m_data.m_value.object->end())
{
m_data.m_value.object->erase(it);
@@ -3377,7 +3397,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
size_type erase(KeyType && key)
{
return erase_internal(std::forward<KeyType>(key));
return erase_internal(lookup_key(std::forward<KeyType>(key)));
}
/// @brief remove element from a JSON array given an index
@@ -3447,7 +3467,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
if (is_object())
{
result.m_it.object_iterator = m_data.m_value.object->find(std::forward<KeyType>(key));
result.m_it.object_iterator = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
}
return result;
@@ -3463,7 +3483,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
if (is_object())
{
result.m_it.object_iterator = m_data.m_value.object->find(std::forward<KeyType>(key));
result.m_it.object_iterator = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
}
return result;
@@ -3486,7 +3506,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
size_type count(KeyType && key) const
{
// return 0 for all nonobject types
return is_object() ? m_data.m_value.object->count(std::forward<KeyType>(key)) : 0;
return is_object() ? m_data.m_value.object->count(lookup_key(std::forward<KeyType>(key))) : 0;
}
/// @brief check the existence of an element in a JSON object
@@ -3504,7 +3524,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_HEDLEY_WARN_UNUSED_RESULT
bool contains(KeyType && key) const
{
return is_object() && m_data.m_value.object->find(std::forward<KeyType>(key)) != m_data.m_value.object->end();
return is_object() && m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key))) != m_data.m_value.object->end();
}
/// @brief check the existence of an element in a JSON object given a JSON pointer
+58 -16
View File
@@ -4735,7 +4735,7 @@ std::is_constructible <decltype(std::declval<Compare>()(std::declval<A>(), std::
// avoid their instantiation on all compilers, even when the first operand
// is false. The dispatch on is_json_pointer_of can be removed once the
// deprecated json_pointer comparison operators have been removed.
template<typename Compare, typename A, typename B, bool = is_json_pointer_of<uncvref_t<A>, uncvref_t<B>>::value>
template<typename Compare, typename A, typename B, bool = is_json_pointer_of<A, B>::value>
struct is_comparable : std::false_type {};
template<typename Compare, typename A, typename B>
@@ -4758,6 +4758,30 @@ using is_usable_as_key_type = typename std::conditional <
std::true_type,
std::false_type >::type;
#ifdef JSON_HAS_CPP_17
// type trait to check if KeyType can only be used as an object key after
// converting it to std::string_view: it is convertible to std::string_view, the
// object's comparator cannot compare it with object_t::key_type directly, but
// can compare a std::string_view. JSON pointers and JSON iterators are ruled out
// first, so that the conversion checks are never instantiated for them (a JSON
// pointer's deprecated conversion to string_t would be named otherwise).
template < typename BasicJsonType, typename KeyTypeCVRef, typename KeyType = uncvref_t<KeyTypeCVRef>,
bool = is_json_pointer<KeyType>::value || is_json_iterator_of<BasicJsonType, KeyType>::value >
struct is_string_view_convertible_key_type : std::false_type {};
template<typename BasicJsonType, typename KeyTypeCVRef, typename KeyType>
struct is_string_view_convertible_key_type<BasicJsonType, KeyTypeCVRef, KeyType, false>
: std::integral_constant < bool,
std::is_convertible<KeyTypeCVRef, std::string_view>::value
&& !is_usable_as_key_type<typename BasicJsonType::object_comparator_t,
typename BasicJsonType::object_t::key_type, KeyTypeCVRef, true, false>::value
&& is_usable_as_key_type<typename BasicJsonType::object_comparator_t,
typename BasicJsonType::object_t::key_type, std::string_view, true, false>::value > {};
#else
template<typename BasicJsonType, typename KeyTypeCVRef>
struct is_string_view_convertible_key_type : std::false_type {};
#endif
// type trait to check if KeyType can be used as an object key
// true if:
// - KeyType is comparable with BasicJsonType::object_t::key_type
@@ -4771,9 +4795,7 @@ using is_usable_as_basic_json_key_type = typename std::conditional <
typename BasicJsonType::object_t::key_type, KeyTypeCVRef,
RequireTransparentComparator, ExcludeObjectKeyType>::value
&& !is_json_iterator_of<BasicJsonType, KeyType>::value)
#ifdef JSON_HAS_CPP_17
|| std::is_convertible<KeyType, std::string_view>::value
#endif
|| is_string_view_convertible_key_type<BasicJsonType, KeyTypeCVRef>::value
, std::true_type,
std::false_type >::type;
@@ -26886,6 +26908,24 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return it;
}
/// @brief the key to look up an object member with: the key itself, or its
/// std::string_view if the object can only be searched with that
template < typename KeyType, detail::enable_if_t <
!detail::is_string_view_convertible_key_type<basic_json_t, KeyType>::value, int > = 0 >
static KeyType && lookup_key(KeyType && key) noexcept
{
return std::forward<KeyType>(key);
}
#ifdef JSON_HAS_CPP_17
template < typename KeyType, detail::enable_if_t <
detail::is_string_view_convertible_key_type<basic_json_t, KeyType>::value, int > = 0 >
static std::string_view lookup_key(KeyType && key)
{
return std::forward<KeyType>(key);
}
#endif
/// @brief erase an element from the object and return the following one
/// Not every map returns an iterator from erase(iterator): some containers
/// (e.g., Abseil's hash maps) return void to avoid computing a successor
@@ -28847,7 +28887,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
}
auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
auto it = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
if (it == m_data.m_value.object->end())
{
JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this));
@@ -28885,7 +28925,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
}
auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
auto it = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
if (it == m_data.m_value.object->end())
{
JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this));
@@ -29021,7 +29061,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// operator[] only works for objects
if (JSON_HEDLEY_LIKELY(is_object()))
{
auto result = m_data.m_value.object->emplace(std::forward<KeyType>(key), nullptr);
auto result = m_data.m_value.object->emplace(lookup_key(std::forward<KeyType>(key)), nullptr);
return set_parent(result.first->second);
}
@@ -29037,7 +29077,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// const operator[] only works for objects
if (JSON_HEDLEY_LIKELY(is_object()))
{
auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
auto it = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
JSON_ASSERT(it != m_data.m_value.object->end());
return it->second;
}
@@ -29047,8 +29087,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
private:
template<typename KeyType>
using is_comparable_with_object_key = detail::is_comparable <
object_comparator_t, const typename object_t::key_type&, KeyType >;
using is_comparable_with_object_key = std::integral_constant < bool,
detail::is_comparable <
object_comparator_t, const typename object_t::key_type&, KeyType >::value
|| detail::is_string_view_convertible_key_type<basic_json_t, KeyType>::value >;
template<typename ValueType>
using value_return_type = std::conditional <
@@ -29431,7 +29473,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
}
const auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
const auto it = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
if (it != m_data.m_value.object->end())
{
m_data.m_value.object->erase(it);
@@ -29458,7 +29500,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
size_type erase(KeyType && key)
{
return erase_internal(std::forward<KeyType>(key));
return erase_internal(lookup_key(std::forward<KeyType>(key)));
}
/// @brief remove element from a JSON array given an index
@@ -29528,7 +29570,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
if (is_object())
{
result.m_it.object_iterator = m_data.m_value.object->find(std::forward<KeyType>(key));
result.m_it.object_iterator = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
}
return result;
@@ -29544,7 +29586,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
if (is_object())
{
result.m_it.object_iterator = m_data.m_value.object->find(std::forward<KeyType>(key));
result.m_it.object_iterator = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
}
return result;
@@ -29567,7 +29609,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
size_type count(KeyType && key) const
{
// return 0 for all nonobject types
return is_object() ? m_data.m_value.object->count(std::forward<KeyType>(key)) : 0;
return is_object() ? m_data.m_value.object->count(lookup_key(std::forward<KeyType>(key))) : 0;
}
/// @brief check the existence of an element in a JSON object
@@ -29585,7 +29627,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_HEDLEY_WARN_UNUSED_RESULT
bool contains(KeyType && key) const
{
return is_object() && m_data.m_value.object->find(std::forward<KeyType>(key)) != m_data.m_value.object->end();
return is_object() && m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key))) != m_data.m_value.object->end();
}
/// @brief check the existence of an element in a JSON object given a JSON pointer
+111
View File
@@ -1892,4 +1892,115 @@ TEST_CASE("operator[] with user-defined std::string_view-convertible types")
}
}
}
TEST_CASE("keys convertible to std::string_view work with all lookup functions (regression test for #5663)")
{
// a key type convertible only to std::string_view: the case #4958 added
// support for, but only the non-const operator[] compiled with it
struct ViewKey
{
operator std::string_view() const
{
return "a";
}
};
// a key type convertible to both std::string and std::string_view: with
// 3.12.0, such a key worked with at, the const operator[], find, count and
// contains via the conversion to std::string; #4958 made the KeyType&&
// templates win overload resolution for it instead, and those then failed
struct DualKey
{
operator std::string() const
{
return "a";
}
operator std::string_view() const
{
return "a";
}
};
SECTION("nlohmann::json")
{
using json = nlohmann::json;
SECTION("ViewKey")
{
json j = {{"a", 1}};
const json& cj = j;
CHECK(j[ViewKey{}] == 1);
CHECK(cj[ViewKey{}] == 1);
CHECK(j.at(ViewKey{}) == 1);
CHECK(cj.at(ViewKey{}) == 1);
CHECK(j.find(ViewKey{}) != j.end());
CHECK(cj.find(ViewKey{}) != cj.end());
CHECK(j.count(ViewKey{}) == 1);
CHECK(j.contains(ViewKey{}));
CHECK(j.value(ViewKey{}, 0) == 1);
CHECK(j.erase(ViewKey{}) == 1);
CHECK(!j.contains("a"));
}
SECTION("DualKey")
{
json j = {{"a", 1}};
const json& cj = j;
CHECK(j[DualKey{}] == 1);
CHECK(cj[DualKey{}] == 1);
CHECK(j.at(DualKey{}) == 1);
CHECK(cj.at(DualKey{}) == 1);
CHECK(j.find(DualKey{}) != j.end());
CHECK(cj.find(DualKey{}) != cj.end());
CHECK(j.count(DualKey{}) == 1);
CHECK(j.contains(DualKey{}));
CHECK(j.value(DualKey{}, 0) == 1);
CHECK(j.erase(DualKey{}) == 1);
CHECK(!j.contains("a"));
}
}
SECTION("nlohmann::ordered_json")
{
using ordered_json = nlohmann::ordered_json;
SECTION("ViewKey")
{
ordered_json j = {{"a", 1}};
const ordered_json& cj = j;
CHECK(j[ViewKey{}] == 1);
CHECK(cj[ViewKey{}] == 1);
CHECK(j.at(ViewKey{}) == 1);
CHECK(cj.at(ViewKey{}) == 1);
CHECK(j.find(ViewKey{}) != j.end());
CHECK(cj.find(ViewKey{}) != cj.end());
CHECK(j.count(ViewKey{}) == 1);
CHECK(j.contains(ViewKey{}));
CHECK(j.value(ViewKey{}, 0) == 1);
CHECK(j.erase(ViewKey{}) == 1);
CHECK(!j.contains("a"));
}
SECTION("DualKey")
{
ordered_json j = {{"a", 1}};
const ordered_json& cj = j;
CHECK(j[DualKey{}] == 1);
CHECK(cj[DualKey{}] == 1);
CHECK(j.at(DualKey{}) == 1);
CHECK(cj.at(DualKey{}) == 1);
CHECK(j.find(DualKey{}) != j.end());
CHECK(cj.find(DualKey{}) != cj.end());
CHECK(j.count(DualKey{}) == 1);
CHECK(j.contains(DualKey{}));
CHECK(j.value(DualKey{}, 0) == 1);
CHECK(j.erase(DualKey{}) == 1);
CHECK(!j.contains("a"));
}
}
}
#endif
-19
View File
@@ -858,25 +858,6 @@ TEST_CASE("JSON pointers")
}
}
SECTION("value(json_pointer, default) with ordered_json #5664")
{
// ordered_json's transparent object comparator made value()'s
// is_comparable_with_object_key check (which passes the pointer as
// a reference) instantiate the deprecated json_pointer/string
// comparison; this must compile without relying on it. The
// deprecation warning itself is not observable here, since the
// unit test build disables -Wdeprecated-declarations (see
// cmake/clang_flags.cmake); it was checked manually instead.
const nlohmann::ordered_json j = {{"n", 1}, {"s", "text"}};
const nlohmann::ordered_json::json_pointer ptr_n("/n");
const nlohmann::ordered_json::json_pointer ptr_s("/s");
const nlohmann::ordered_json::json_pointer ptr_missing("/missing");
CHECK(j.value(ptr_n, 0) == 1);
CHECK(j.value(ptr_s, std::string("x")) == "text");
CHECK(j.value(ptr_missing, 42) == 42);
}
// build with C++20
// JSON_HAS_CPP_20
#if defined(__cpp_char8_t)