mirror of
https://github.com/nlohmann/json.git
synced 2026-10-03 05:00:30 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc9370c291 | ||
|
|
666801a2c6 |
@@ -224,5 +224,7 @@ Strong exception safety: if an exception occurs, the original value stays intact
|
|||||||
|
|
||||||
1. Added in version 1.0.0.
|
1. Added in version 1.0.0.
|
||||||
2. 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.
|
4. Added in version 2.0.0.
|
||||||
|
|||||||
@@ -119,7 +119,9 @@ Logarithmic in the size of the JSON object.
|
|||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
1. Added in version 3.11.0.
|
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. Added in version 3.7.0.
|
||||||
4. Deleted overloads for integral key types added in version 3.13.0 to reject such calls at compile time instead of
|
4. Deleted overloads for integral key types added in version 3.13.0 to reject such calls at compile time instead of
|
||||||
causing undefined behavior at runtime.
|
causing undefined behavior at runtime.
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ Logarithmic in the size of the JSON object.
|
|||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
1. Added in version 3.11.0.
|
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. Deleted overload for integral key types added in version 3.13.0 to reject such calls at compile time instead of
|
3. Deleted overload for integral key types added in version 3.13.0 to reject such calls at compile time instead of
|
||||||
causing undefined behavior at runtime.
|
causing undefined behavior at runtime.
|
||||||
|
|||||||
@@ -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.
|
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.
|
2. Added in version 1.0.0. Added support for binary types in version 3.8.0.
|
||||||
3. Added in version 1.0.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.
|
5. Added in version 1.0.0.
|
||||||
|
|||||||
@@ -88,6 +88,8 @@ Logarithmic in the size of the JSON object.
|
|||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
1. Added in version 3.11.0.
|
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. Deleted overloads for integral key types added in version 3.13.0 to reject such calls at compile time instead of
|
3. Deleted overloads for integral key types added in version 3.13.0 to reject such calls at compile time instead of
|
||||||
causing undefined behavior at runtime.
|
causing undefined behavior at runtime.
|
||||||
|
|||||||
@@ -195,7 +195,9 @@ changes to any JSON value.
|
|||||||
1. Added in version 1.0.0. Changed parameter `default_value` type from `const ValueType&` to `ValueType&&` in version
|
1. Added in version 1.0.0. Changed parameter `default_value` type from `const ValueType&` to `ValueType&&` in version
|
||||||
3.11.0. Deleted overload for integral key types added in version 3.13.0 to reject such calls at compile time
|
3.11.0. Deleted overload for integral key types added in version 3.13.0 to reject such calls at compile time
|
||||||
instead of causing undefined behavior at runtime.
|
instead of causing undefined behavior at runtime.
|
||||||
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
|
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
|
`ptr` through an array unexpectedly threw `out_of_range` instead of returning the resolved element (or
|
||||||
`default_value`, as documented).
|
`default_value`, as documented).
|
||||||
|
|||||||
@@ -679,29 +679,19 @@ class json_pointer
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// tokens that array_index() rejects with parse_error.106/109
|
// may throw parse_error.106/109 for a malformed index; an
|
||||||
// are passed on to it; all other tokens that it would reject
|
// index that is syntactically valid but cannot be
|
||||||
// with out_of_range.404/410 are detected here, so that this
|
// represented (out_of_range.404/410) is treated like an
|
||||||
// also works without exceptions
|
// out-of-range index below
|
||||||
if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 && !(reference_token[0] >= '1' && reference_token[0] <= '9')))
|
typename BasicJsonType::size_type idx{};
|
||||||
|
JSON_TRY
|
||||||
{
|
{
|
||||||
static_cast<void>(array_index<BasicJsonType>(reference_token)); // throws parse_error.106/109
|
idx = array_index<BasicJsonType>(reference_token);
|
||||||
}
|
}
|
||||||
if (JSON_HEDLEY_UNLIKELY(reference_token.empty() || !std::all_of(reference_token.begin(), reference_token.end(), [](const char c)
|
JSON_INTERNAL_CATCH (detail::out_of_range&)
|
||||||
{
|
|
||||||
return c >= '0' && c <= '9';
|
|
||||||
})))
|
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
errno = 0; // strtoull() does not reset errno on success
|
|
||||||
char* p_end = nullptr; // NOLINT(misc-const-correctness)
|
|
||||||
const unsigned long long magnitude = std::strtoull(reference_token.data(), &p_end, 10); // NOLINT(runtime/int)
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(errno == ERANGE || magnitude >= static_cast<unsigned long long>((std::numeric_limits<typename BasicJsonType::size_type>::max)()))) // NOLINT(runtime/int)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
const auto idx = static_cast<typename BasicJsonType::size_type>(magnitude);
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
|
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -748,6 +748,30 @@ using is_usable_as_key_type = typename std::conditional <
|
|||||||
std::true_type,
|
std::true_type,
|
||||||
std::false_type >::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
|
// type trait to check if KeyType can be used as an object key
|
||||||
// true if:
|
// true if:
|
||||||
// - KeyType is comparable with BasicJsonType::object_t::key_type
|
// - 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,
|
typename BasicJsonType::object_t::key_type, KeyTypeCVRef,
|
||||||
RequireTransparentComparator, ExcludeObjectKeyType>::value
|
RequireTransparentComparator, ExcludeObjectKeyType>::value
|
||||||
&& !is_json_iterator_of<BasicJsonType, KeyType>::value)
|
&& !is_json_iterator_of<BasicJsonType, KeyType>::value)
|
||||||
#ifdef JSON_HAS_CPP_17
|
|| is_string_view_convertible_key_type<BasicJsonType, KeyTypeCVRef>::value
|
||||||
|| std::is_convertible<KeyType, std::string_view>::value
|
|
||||||
#endif
|
|
||||||
, std::true_type,
|
, std::true_type,
|
||||||
std::false_type >::type;
|
std::false_type >::type;
|
||||||
|
|
||||||
|
|||||||
+32
-12
@@ -807,6 +807,24 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return it;
|
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
|
/// @brief erase an element from the object and return the following one
|
||||||
/// Not every map returns an iterator from erase(iterator): some containers
|
/// Not every map returns an iterator from erase(iterator): some containers
|
||||||
/// (e.g., Abseil's hash maps) return void to avoid computing a successor
|
/// (e.g., Abseil's hash maps) return void to avoid computing a successor
|
||||||
@@ -2767,7 +2785,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));
|
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())
|
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));
|
JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this));
|
||||||
@@ -2805,7 +2823,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));
|
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())
|
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));
|
JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this));
|
||||||
@@ -2948,7 +2966,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// operator[] only works for objects
|
// operator[] only works for objects
|
||||||
if (JSON_HEDLEY_LIKELY(is_object()))
|
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);
|
return set_parent(result.first->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2964,7 +2982,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// const operator[] only works for objects
|
// const operator[] only works for objects
|
||||||
if (JSON_HEDLEY_LIKELY(is_object()))
|
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());
|
JSON_ASSERT(it != m_data.m_value.object->end());
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
@@ -2974,8 +2992,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
template<typename KeyType>
|
template<typename KeyType>
|
||||||
using is_comparable_with_object_key = detail::is_comparable <
|
using is_comparable_with_object_key = std::integral_constant < bool,
|
||||||
object_comparator_t, const typename object_t::key_type&, KeyType >;
|
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>
|
template<typename ValueType>
|
||||||
using value_return_type = std::conditional <
|
using value_return_type = std::conditional <
|
||||||
@@ -3362,7 +3382,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));
|
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())
|
if (it != m_data.m_value.object->end())
|
||||||
{
|
{
|
||||||
m_data.m_value.object->erase(it);
|
m_data.m_value.object->erase(it);
|
||||||
@@ -3389,7 +3409,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>
|
detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
|
||||||
size_type erase(KeyType && key)
|
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
|
/// @brief remove element from a JSON array given an index
|
||||||
@@ -3469,7 +3489,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
|
|
||||||
if (is_object())
|
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;
|
return result;
|
||||||
@@ -3485,7 +3505,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
|
|
||||||
if (is_object())
|
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;
|
return result;
|
||||||
@@ -3508,7 +3528,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
size_type count(KeyType && key) const
|
size_type count(KeyType && key) const
|
||||||
{
|
{
|
||||||
// return 0 for all nonobject types
|
// 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
|
/// @brief check the existence of an element in a JSON object
|
||||||
@@ -3526,7 +3546,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
bool contains(KeyType && key) const
|
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
|
/// @brief check the existence of an element in a JSON object given a JSON pointer
|
||||||
|
|||||||
@@ -4756,6 +4756,30 @@ using is_usable_as_key_type = typename std::conditional <
|
|||||||
std::true_type,
|
std::true_type,
|
||||||
std::false_type >::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
|
// type trait to check if KeyType can be used as an object key
|
||||||
// true if:
|
// true if:
|
||||||
// - KeyType is comparable with BasicJsonType::object_t::key_type
|
// - KeyType is comparable with BasicJsonType::object_t::key_type
|
||||||
@@ -4769,9 +4793,7 @@ using is_usable_as_basic_json_key_type = typename std::conditional <
|
|||||||
typename BasicJsonType::object_t::key_type, KeyTypeCVRef,
|
typename BasicJsonType::object_t::key_type, KeyTypeCVRef,
|
||||||
RequireTransparentComparator, ExcludeObjectKeyType>::value
|
RequireTransparentComparator, ExcludeObjectKeyType>::value
|
||||||
&& !is_json_iterator_of<BasicJsonType, KeyType>::value)
|
&& !is_json_iterator_of<BasicJsonType, KeyType>::value)
|
||||||
#ifdef JSON_HAS_CPP_17
|
|| is_string_view_convertible_key_type<BasicJsonType, KeyTypeCVRef>::value
|
||||||
|| std::is_convertible<KeyType, std::string_view>::value
|
|
||||||
#endif
|
|
||||||
, std::true_type,
|
, std::true_type,
|
||||||
std::false_type >::type;
|
std::false_type >::type;
|
||||||
|
|
||||||
@@ -20291,29 +20313,19 @@ class json_pointer
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// tokens that array_index() rejects with parse_error.106/109
|
// may throw parse_error.106/109 for a malformed index; an
|
||||||
// are passed on to it; all other tokens that it would reject
|
// index that is syntactically valid but cannot be
|
||||||
// with out_of_range.404/410 are detected here, so that this
|
// represented (out_of_range.404/410) is treated like an
|
||||||
// also works without exceptions
|
// out-of-range index below
|
||||||
if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 && !(reference_token[0] >= '1' && reference_token[0] <= '9')))
|
typename BasicJsonType::size_type idx{};
|
||||||
|
JSON_TRY
|
||||||
{
|
{
|
||||||
static_cast<void>(array_index<BasicJsonType>(reference_token)); // throws parse_error.106/109
|
idx = array_index<BasicJsonType>(reference_token);
|
||||||
}
|
}
|
||||||
if (JSON_HEDLEY_UNLIKELY(reference_token.empty() || !std::all_of(reference_token.begin(), reference_token.end(), [](const char c)
|
JSON_INTERNAL_CATCH (detail::out_of_range&)
|
||||||
{
|
|
||||||
return c >= '0' && c <= '9';
|
|
||||||
})))
|
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
errno = 0; // strtoull() does not reset errno on success
|
|
||||||
char* p_end = nullptr; // NOLINT(misc-const-correctness)
|
|
||||||
const unsigned long long magnitude = std::strtoull(reference_token.data(), &p_end, 10); // NOLINT(runtime/int)
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(errno == ERANGE || magnitude >= static_cast<unsigned long long>((std::numeric_limits<typename BasicJsonType::size_type>::max)()))) // NOLINT(runtime/int)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
const auto idx = static_cast<typename BasicJsonType::size_type>(magnitude);
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
|
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
|
||||||
{
|
{
|
||||||
@@ -27744,6 +27756,24 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return it;
|
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
|
/// @brief erase an element from the object and return the following one
|
||||||
/// Not every map returns an iterator from erase(iterator): some containers
|
/// Not every map returns an iterator from erase(iterator): some containers
|
||||||
/// (e.g., Abseil's hash maps) return void to avoid computing a successor
|
/// (e.g., Abseil's hash maps) return void to avoid computing a successor
|
||||||
@@ -29704,7 +29734,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));
|
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())
|
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));
|
JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this));
|
||||||
@@ -29742,7 +29772,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));
|
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())
|
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));
|
JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this));
|
||||||
@@ -29885,7 +29915,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// operator[] only works for objects
|
// operator[] only works for objects
|
||||||
if (JSON_HEDLEY_LIKELY(is_object()))
|
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);
|
return set_parent(result.first->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29901,7 +29931,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// const operator[] only works for objects
|
// const operator[] only works for objects
|
||||||
if (JSON_HEDLEY_LIKELY(is_object()))
|
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());
|
JSON_ASSERT(it != m_data.m_value.object->end());
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
@@ -29911,8 +29941,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
template<typename KeyType>
|
template<typename KeyType>
|
||||||
using is_comparable_with_object_key = detail::is_comparable <
|
using is_comparable_with_object_key = std::integral_constant < bool,
|
||||||
object_comparator_t, const typename object_t::key_type&, KeyType >;
|
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>
|
template<typename ValueType>
|
||||||
using value_return_type = std::conditional <
|
using value_return_type = std::conditional <
|
||||||
@@ -30299,7 +30331,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));
|
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())
|
if (it != m_data.m_value.object->end())
|
||||||
{
|
{
|
||||||
m_data.m_value.object->erase(it);
|
m_data.m_value.object->erase(it);
|
||||||
@@ -30326,7 +30358,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>
|
detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
|
||||||
size_type erase(KeyType && key)
|
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
|
/// @brief remove element from a JSON array given an index
|
||||||
@@ -30406,7 +30438,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
|
|
||||||
if (is_object())
|
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;
|
return result;
|
||||||
@@ -30422,7 +30454,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
|
|
||||||
if (is_object())
|
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;
|
return result;
|
||||||
@@ -30445,7 +30477,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
size_type count(KeyType && key) const
|
size_type count(KeyType && key) const
|
||||||
{
|
{
|
||||||
// return 0 for all nonobject types
|
// 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
|
/// @brief check the existence of an element in a JSON object
|
||||||
@@ -30463,7 +30495,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
bool contains(KeyType && key) const
|
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
|
/// @brief check the existence of an element in a JSON object given a JSON pointer
|
||||||
|
|||||||
@@ -47,21 +47,6 @@ TEST_CASE("Tests with disabled exceptions")
|
|||||||
delete sax_no_exception::error_string; // NOLINT(cppcoreguidelines-owning-memory)
|
delete sax_no_exception::error_string; // NOLINT(cppcoreguidelines-owning-memory)
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("issue #5672 - value(json_pointer, default) must not abort for array tokens that are not a valid index")
|
|
||||||
{
|
|
||||||
const json j = {1, 2, 3};
|
|
||||||
|
|
||||||
// a syntactically valid index that is out of range for this array
|
|
||||||
CHECK(j.value("/7"_json_pointer, 42) == 42);
|
|
||||||
// a reference token that is not a number at all
|
|
||||||
CHECK(j.value("/1a"_json_pointer, 42) == 42);
|
|
||||||
// the empty reference token (JSON pointer "/")
|
|
||||||
CHECK(j.value("/"_json_pointer, 42) == 42);
|
|
||||||
// an index whose magnitude does not fit into size_type
|
|
||||||
CHECK(j.value("/99999999999999999999999"_json_pointer, 42) == 42);
|
|
||||||
CHECK(j.value("/18446744073709551615"_json_pointer, 42) == 42);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("growing an ordered_json object")
|
SECTION("growing an ordered_json object")
|
||||||
{
|
{
|
||||||
auto j = nlohmann::ordered_json::object();
|
auto j = nlohmann::ordered_json::object();
|
||||||
|
|||||||
@@ -516,21 +516,6 @@ TEST_CASE_TEMPLATE("element access 2", Json, nlohmann::json, nlohmann::ordered_j
|
|||||||
CHECK(j_array.value("/-"_json_pointer, 42) == 42);
|
CHECK(j_array.value("/-"_json_pointer, 42) == 42);
|
||||||
CHECK(j_array_const.value("/-"_json_pointer, 42) == 42);
|
CHECK(j_array_const.value("/-"_json_pointer, 42) == 42);
|
||||||
|
|
||||||
// Test an index with a non-digit after a valid leading digit; this is
|
|
||||||
// out_of_range (not parse_error) and must not throw (see #5672)
|
|
||||||
CHECK(j_array.value("/1a"_json_pointer, 42) == 42);
|
|
||||||
CHECK(j_array_const.value("/1a"_json_pointer, 42) == 42);
|
|
||||||
|
|
||||||
// Test the empty reference token (JSON pointer "/"); see #5672
|
|
||||||
CHECK(j_array.value("/"_json_pointer, 42) == 42);
|
|
||||||
CHECK(j_array_const.value("/"_json_pointer, 42) == 42);
|
|
||||||
|
|
||||||
// Test an index whose magnitude does not fit into size_type (see #5672)
|
|
||||||
CHECK(j_array.value("/99999999999999999999999"_json_pointer, 42) == 42);
|
|
||||||
CHECK(j_array_const.value("/99999999999999999999999"_json_pointer, 42) == 42);
|
|
||||||
CHECK(j_array.value("/18446744073709551615"_json_pointer, 42) == 42);
|
|
||||||
CHECK(j_array_const.value("/18446744073709551615"_json_pointer, 42) == 42);
|
|
||||||
|
|
||||||
#if !defined(JSON_NOEXCEPTION)
|
#if !defined(JSON_NOEXCEPTION)
|
||||||
// Test malformed index (non-numeric) throws parse_error
|
// Test malformed index (non-numeric) throws parse_error
|
||||||
CHECK_THROWS_WITH_AS(j_array.value("/foo"_json_pointer, 1), "[json.exception.parse_error.109] parse error: array index 'foo' is not a number", typename Json::parse_error&);
|
CHECK_THROWS_WITH_AS(j_array.value("/foo"_json_pointer, 1), "[json.exception.parse_error.109] parse error: array index 'foo' is not a number", typename Json::parse_error&);
|
||||||
@@ -1988,4 +1973,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
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user