diff --git a/doc/mkdocs/docs/api/basic_json/at.md b/doc/mkdocs/docs/api/basic_json/at.md index fc518f46e4..e4123cdad1 100644 --- a/doc/mkdocs/docs/api/basic_json/at.md +++ b/doc/mkdocs/docs/api/basic_json/at.md @@ -6,8 +6,10 @@ reference at(size_type idx); const_reference at(size_type idx) const; // (2) -reference at(const typename object_t::key_type& key); -const_reference at(const typename object_t::key_type& key) const; +template +reference at(const KeyType& key); +template +const_reference at(const KeyType& key) const; // (3) reference at(const json_pointer& ptr); @@ -18,6 +20,13 @@ const_reference at(const json_pointer& ptr) const; 2. Returns a reference to the object element at with specified key `key`, with bounds checking. 3. Returns a reference to the element at with specified JSON pointer `ptr`, with bounds checking. +## Template parameters + +`KeyType` +: A type for an object key other than [`json_pointer`](../json_pointer/index.md) that is comparable with [`string_t`](string_t.md) + using [`object_comparator_t`](object_comparator_t.md). + This can also be a string literal or a string view (C++17). + ## Parameters `idx` (in) @@ -172,5 +181,5 @@ Strong exception safety: if an exception occurs, the original value stays intact ## Version history 1. Added in version 1.0.0. -2. Added in version 1.0.0. +2. Added in version 1.0.0. Added `KeyType` template in version 3.11.0. 3. Added in version 2.0.0. diff --git a/doc/mkdocs/docs/api/basic_json/contains.md b/doc/mkdocs/docs/api/basic_json/contains.md index 8463f4ea98..cb83de3913 100644 --- a/doc/mkdocs/docs/api/basic_json/contains.md +++ b/doc/mkdocs/docs/api/basic_json/contains.md @@ -2,8 +2,8 @@ ```cpp // (1) -template -bool contains(KeyT && key) const; +template +bool contains(const KeyType& key) const; // (2) bool contains(const json_pointer& ptr) const; @@ -15,8 +15,10 @@ bool contains(const json_pointer& ptr) const; ## Template parameters -`KeyT` -: A type for an object key other than `basic_json::json_pointer`. +`KeyType` +: A type for an object key other than [`json_pointer`](../json_pointer/index.md) that is comparable with [`string_t`](string_t.md) + using [`object_comparator_t`](object_comparator_t.md). + This can also be a string literal or a string view (C++17). ## Parameters @@ -90,5 +92,5 @@ Logarithmic in the size of the JSON object. ## Version history -1. Added in version 3.6.0. +1. Added in version 3.6.0. Extended template `KeyType` to support any comparable type in version 3.11.0. 2. Added in version 3.7.0. diff --git a/doc/mkdocs/docs/api/basic_json/count.md b/doc/mkdocs/docs/api/basic_json/count.md index fcfef86737..71439ea542 100644 --- a/doc/mkdocs/docs/api/basic_json/count.md +++ b/doc/mkdocs/docs/api/basic_json/count.md @@ -1,8 +1,8 @@ # nlohmann::basic_json::count ```cpp -template -size_type count(KeyT&& key) const; +template +size_type count(const KeyType& key) const; ``` Returns the number of elements with key `key`. If `ObjectType` is the default `std::map` type, the return value will @@ -10,8 +10,10 @@ always be `0` (`key` was not found) or `1` (`key` was found). ## Template parameters -`KeyT` -: A type for an object key. +`KeyType` +: A type for an object key other than [`json_pointer`](../json_pointer/index.md) that is comparable with [`string_t`](string_t.md) + using [`object_comparator_t`](object_comparator_t.md). + This can also be a string literal or a string view (C++17). ## Parameters @@ -53,3 +55,4 @@ This method always returns `0` when executed on a JSON type that is not an objec ## Version history - Added in version 1.0.0. +- Extended template `KeyType` to support any comparable type in version 3.11.0. diff --git a/doc/mkdocs/docs/api/basic_json/default_object_comparator_t.md b/doc/mkdocs/docs/api/basic_json/default_object_comparator_t.md index 97b0354223..265a9d385f 100644 --- a/doc/mkdocs/docs/api/basic_json/default_object_comparator_t.md +++ b/doc/mkdocs/docs/api/basic_json/default_object_comparator_t.md @@ -15,4 +15,4 @@ The actual comparator used depends on [`object_t`](object_t.md) and can be obtai ## Version history -- Added in version 3.10.6. +- Added in version 3.11.0. diff --git a/doc/mkdocs/docs/api/basic_json/erase.md b/doc/mkdocs/docs/api/basic_json/erase.md index d94c25b7fd..f377605f56 100644 --- a/doc/mkdocs/docs/api/basic_json/erase.md +++ b/doc/mkdocs/docs/api/basic_json/erase.md @@ -10,7 +10,8 @@ iterator erase(iterator first, iterator last); const_iterator erase(const_iterator first, const_iterator last); // (3) -size_type erase(const typename object_t::key_type& key); +template +size_type erase(const KeyType& key); // (4) void erase(const size_type idx); @@ -31,6 +32,13 @@ void erase(const size_type idx); 4. Removes an element from a JSON array by index. +## Template parameters + +`KeyType` +: A type for an object key other than [`json_pointer`](../json_pointer/index.md) that is comparable with [`string_t`](string_t.md) + using [`object_comparator_t`](object_comparator_t.md). + This can also be a string literal or a string view (C++17). + ## Parameters `pos` (in) @@ -174,3 +182,4 @@ Strong exception safety: if an exception occurs, the original value stays intact - Added in version 1.0.0. - Added support for binary types in version 3.8.0. +- Added `KeyType` template in version 3.11.0. diff --git a/doc/mkdocs/docs/api/basic_json/find.md b/doc/mkdocs/docs/api/basic_json/find.md index af4cb29720..5376011671 100644 --- a/doc/mkdocs/docs/api/basic_json/find.md +++ b/doc/mkdocs/docs/api/basic_json/find.md @@ -1,11 +1,11 @@ # nlohmann::basic_json::find ```cpp -template -iterator find(KeyT&& key); +template +iterator find(const KeyType& key); -template -const_iterator find(KeyT&& key) const; +template +const_iterator find(const KeyType& key) const; ``` Finds an element in a JSON object with key equivalent to `key`. If the element is not found or the JSON value is not an @@ -13,8 +13,10 @@ object, `end()` is returned. ## Template parameters -`KeyT` -: A type for an object key. +`KeyType` +: A type for an object key other than [`json_pointer`](../json_pointer/index.md) that is comparable with [`string_t`](string_t.md) + using [`object_comparator_t`](object_comparator_t.md). + This can also be a string literal or a string view (C++17). ## Parameters @@ -61,3 +63,4 @@ This method always returns `end()` when executed on a JSON type that is not an o ## Version history - Added in version 1.0.0. +- Extended template `KeyType` to support any comparable type and changed from rvalue reference to const lvalue reference in version 3.11.0. diff --git a/doc/mkdocs/docs/api/basic_json/operator[].md b/doc/mkdocs/docs/api/basic_json/operator[].md index 5b6512a21c..4e20d15244 100644 --- a/doc/mkdocs/docs/api/basic_json/operator[].md +++ b/doc/mkdocs/docs/api/basic_json/operator[].md @@ -6,12 +6,10 @@ reference operator[](size_type idx); const_reference operator[](size_type idx) const; // (2) -reference operator[](const typename object_t::key_type& key); -const_reference operator[](const typename object_t::key_type& key) const; -template -reference operator[](T* key); -template -const_reference operator[](T* key) const; +template +reference operator[](KeyType && key); +template +const_reference operator[](KeyType && key) const; // (3) reference operator[](const json_pointer& ptr); @@ -19,13 +17,15 @@ const_reference operator[](const json_pointer& ptr) const; ``` 1. Returns a reference to the array element at specified location `idx`. -2. Returns a reference to the object element at with specified key `key`. -3. Returns a reference to the element at with specified JSON pointer `ptr`. +2. Returns a reference to the object element with specified key `key`. +3. Returns a reference to the element with specified JSON pointer `ptr`. ## Template parameters -`T` -: string literal convertible to `object_t::key_type` +`KeyType` +: A type for an object key other than [`json_pointer`](../json_pointer/index.md) that is comparable with [`string_t`](string_t.md) + using [`object_comparator_t`](object_comparator_t.md). + This can also be a string literal or a string view (C++17). ## Parameters @@ -192,5 +192,6 @@ Strong exception safety: if an exception occurs, the original value stays intact ## Version history 1. Added in version 1.0.0. -2. Added in version 1.0.0. Overloads for `T* key` added in version 1.1.0. +2. Added in version 1.0.0. Overloads for `T* key` added in version 1.1.0. Template `T* key` replaced by template `KeyType` + in version 3.11.0 which now also supports `std::string_view`. 3. Added in version 2.0.0. diff --git a/doc/mkdocs/docs/api/basic_json/value.md b/doc/mkdocs/docs/api/basic_json/value.md index 0b4f1cc19a..003d86cbd7 100644 --- a/doc/mkdocs/docs/api/basic_json/value.md +++ b/doc/mkdocs/docs/api/basic_json/value.md @@ -2,9 +2,9 @@ ```cpp // (1) -template -ValueType value(const typename object_t::key_type& key, - const ValueType& default_value) const; +template +ValueType value(const KeyType& key, + ValueType && default_value) const; // (2) template @@ -44,6 +44,10 @@ ValueType value(const json_pointer& ptr, ## Template parameters +`KeyType` +: A type for an object key other than [`json_pointer`](../json_pointer/index.md) that is comparable with [`string_t`](string_t.md) + using [`object_comparator_t`](object_comparator_t.md). + This can also be a string literal or a string view (C++17). `ValueType` : type compatible to JSON values, for instance `#!cpp int` for JSON integer numbers, `#!cpp bool` for JSON booleans, or `#!cpp std::vector` types for JSON arrays. Note the type of the expected value at `key`/`ptr` and the default @@ -125,5 +129,5 @@ changes to any JSON value. ## Version history -1. Added in version 1.0.0. +1. Added in version 1.0.0. Added `KeyType` template parameter in 3.11.0. 2. Added in version 2.0.2. diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index ba94d4e3f8..61b0e5e5c9 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -2006,22 +2006,38 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec reference at(const typename object_t::key_type& key) { // at only works for objects - if (JSON_HEDLEY_LIKELY(is_object())) + if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_TRY - { - return set_parent(m_value.object->at(key)); - } - JSON_CATCH (std::out_of_range&) - { - // create better exception explanation - JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this)); - } + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); } - else + + auto it = m_value.object->find(key); + if (it == m_value.object->end()) + { + JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this)); + } + return set_parent(it->second); + } + + /// @brief access specified object element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + template < class KeyType, detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0 > + reference at(KeyType && key) + { + // at only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) { JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); } + + auto it = m_value.object->find(std::forward(key)); + if (it == m_value.object->end()) + { + // should key be forwarded here? (might call move ctor) + JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(key), "' not found"), this)); + } + return set_parent(it->second); } /// @brief access specified object element with bounds checking @@ -2029,22 +2045,37 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const_reference at(const typename object_t::key_type& key) const { // at only works for objects - if (JSON_HEDLEY_LIKELY(is_object())) + if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_TRY - { - return m_value.object->at(key); - } - JSON_CATCH (std::out_of_range&) - { - // create better exception explanation - JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this)); - } + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); } - else + + auto it = m_value.object->find(key); + if (it == m_value.object->end()) + { + JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(key), "' not found"), this)); + } + return it->second; + } + + /// @brief access specified object element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + template < class KeyType, detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0 > + const_reference at(KeyType && key) const + { + // at only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) { JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); } + + auto it = m_value.object->find(std::forward(key)); + if (it == m_value.object->end()) + { + JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(key), "' not found"), this)); + } + return it->second; } /// @brief access specified array element @@ -2108,7 +2139,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief access specified object element /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ - reference operator[](const typename object_t::key_type& key) + template < class KeyType, detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0 > + reference operator[](KeyType && key) { // implicitly convert null value to an empty object if (is_null()) @@ -2121,7 +2154,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - return set_parent(m_value.object->operator[](key)); + auto result = m_value.object->emplace(std::forward(key), nullptr); + return set_parent(result.first->second); } JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); @@ -2129,13 +2163,16 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief access specified object element /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ - const_reference operator[](const typename object_t::key_type& key) const + template < class KeyType, detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0 > + const_reference operator[](KeyType && key) const { // const operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); - return m_value.object->find(key)->second; + auto it = m_value.object->find(std::forward(key)); + JSON_ASSERT(it != m_value.object->end()); + return it->second; } JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); @@ -2155,7 +2192,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec assert_invariant(); } - // at only works for objects + // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { return set_parent(m_value.object->operator[](key)); @@ -2170,7 +2207,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_HEDLEY_NON_NULL(2) const_reference operator[](T* key) const { - // at only works for objects + // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); @@ -2183,22 +2220,23 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief access specified object element with default value /// @sa https://json.nlohmann.me/api/basic_json/value/ /// using std::is_convertible in a std::enable_if will fail when using explicit conversions - template < class ValueType, typename std::enable_if < + template < class KeyType, class ValueType, detail::enable_if_t < detail::is_getable::value - && !std::is_same::value, int >::type = 0 > - ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const + && !std::is_same::value + && !detail::is_json_pointer::value, int > = 0 > + typename std::decay::type value(KeyType && key, ValueType && default_value) const { - // at only works for objects + // value only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { // if key is found, return value and given default value otherwise - const auto it = find(key); + const auto it = find(std::forward(key)); if (it != end()) { - return it->template get(); + return it->template get::type>(); } - return default_value; + return std::forward(default_value); } JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); @@ -2207,18 +2245,20 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief access specified object element with default value /// @sa https://json.nlohmann.me/api/basic_json/value/ /// overload for a default value of type const char* - string_t value(const typename object_t::key_type& key, const char* default_value) const + template < class KeyType, detail::enable_if_t < + !detail::is_json_pointer::value, int > = 0 > + string_t value(KeyType && key, const char* default_value) const { - return value(key, string_t(default_value)); + return value(std::forward(key), string_t(default_value)); } /// @brief access specified object element via JSON Pointer with default value /// @sa https://json.nlohmann.me/api/basic_json/value/ - template::value, int>::type = 0> + template < class ValueType, detail::enable_if_t < + detail::is_getable::value, int> = 0 > ValueType value(const json_pointer& ptr, const ValueType& default_value) const { - // at only works for objects + // value only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { // if pointer resolves a value, return it or use default value @@ -2235,8 +2275,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); } - template::value, int>::type = 0> + template < class ValueType, class BasicJsonType, detail::enable_if_t < + detail::is_getable::value, int> = 0 > JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) ValueType value(const ::nlohmann::json_pointer& ptr, const ValueType& default_value) const { @@ -2294,10 +2334,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief remove element given an iterator /// @sa https://json.nlohmann.me/api/basic_json/erase/ - template < class IteratorType, typename std::enable_if < + template < class IteratorType, detail::enable_if_t < std::is_same::value || - std::is_same::value, int >::type - = 0 > + std::is_same::value, int > = 0 > IteratorType erase(IteratorType pos) { // make sure iterator fits the current value @@ -2365,10 +2404,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief remove elements given an iterator range /// @sa https://json.nlohmann.me/api/basic_json/erase/ - template < class IteratorType, typename std::enable_if < + template < class IteratorType, detail::enable_if_t < std::is_same::value || - std::is_same::value, int >::type - = 0 > + std::is_same::value, int > = 0 > IteratorType erase(IteratorType first, IteratorType last) { // make sure iterator fits the current value @@ -2437,17 +2475,57 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return result; } - /// @brief remove element from a JSON object given a key - /// @sa https://json.nlohmann.me/api/basic_json/erase/ - size_type erase(const typename object_t::key_type& key) + private: + template < typename KeyType, detail::enable_if_t < + detail::has_erase_with_key_type::value, int > = 0 > + size_type erase_internal(KeyType && key) { // this erase only works for objects - if (JSON_HEDLEY_LIKELY(is_object())) + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); + } + + return m_value.object->erase(std::forward(key)); + } + + template < typename KeyType, detail::enable_if_t < + !detail::has_erase_with_key_type::value, int > = 0 > + size_type erase_internal(KeyType && key) + { + // this erase only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); + } + + const auto it = m_value.object->find(std::forward(key)); + if (it != m_value.object->end()) { - return m_value.object->erase(key); + m_value.object->erase(it); + return 1; } + return 0; + } + + public: - JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); + /// @brief remove element from a JSON object given a key + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + size_type erase(const typename object_t::key_type& key) + { + // the indirection via erase_internal() is needed to avoid making this + // function a template and thus de-rank it during overload resolution + return erase_internal(key); + } + + /// @brief remove element from a JSON object given a key + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + template < class KeyType, detail::enable_if_t < + detail::is_usable_as_key_type::value, int > = 0 > + size_type erase(KeyType && key) + { + return erase_internal(std::forward(key)); } /// @brief remove element from a JSON array given an index @@ -2482,14 +2560,41 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief find an element in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/find/ - template - iterator find(KeyT&& key) + iterator find(const typename object_t::key_type& key) + { + auto result = end(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(key); + } + + return result; + } + + template < class KeyType, detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0> + iterator find(KeyType && key) { auto result = end(); if (is_object()) { - result.m_it.object_iterator = m_value.object->find(std::forward(key)); + result.m_it.object_iterator = m_value.object->find(std::forward(key)); + } + + return result; + } + + /// @brief find an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/find/ + const_iterator find(const typename object_t::key_type& key) const + { + auto result = cend(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(key); } return result; @@ -2497,14 +2602,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief find an element in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/find/ - template - const_iterator find(KeyT&& key) const + template < class KeyType, detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0 > + const_iterator find(KeyType && key) const { auto result = cend(); if (is_object()) { - result.m_it.object_iterator = m_value.object->find(std::forward(key)); + result.m_it.object_iterator = m_value.object->find(std::forward(key)); } return result; @@ -2512,20 +2618,36 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief returns the number of occurrences of a key in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/count/ - template - size_type count(KeyT&& key) const + size_type count(const typename object_t::key_type& key) const { // return 0 for all nonobject types - return is_object() ? m_value.object->count(std::forward(key)) : 0; + return is_object() ? m_value.object->count(key) : 0; + } + + /// @brief returns the number of occurrences of a key in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/count/ + template < class KeyType, detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0 > + size_type count(KeyType && key) const + { + // return 0 for all nonobject types + return is_object() ? m_value.object->count(std::forward(key)) : 0; + } + + /// @brief check the existence of an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/contains/ + bool contains(const typename object_t::key_type& key) const + { + return is_object() && m_value.object->find(key) != m_value.object->end(); } /// @brief check the existence of an element in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/contains/ - template < typename KeyT, typename std::enable_if < - !detail::is_json_pointer::type>::value, int >::type = 0 > - bool contains(KeyT && key) const + template < class KeyType, detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0 > + bool contains(KeyType && key) const { - return is_object() && m_value.object->find(std::forward(key)) != m_value.object->end(); + return is_object() && m_value.object->find(std::forward(key)) != m_value.object->end(); } /// @brief check the existence of an element in a JSON object given a JSON pointer diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 0967c48797..e64ff40329 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -19546,22 +19546,38 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec reference at(const typename object_t::key_type& key) { // at only works for objects - if (JSON_HEDLEY_LIKELY(is_object())) + if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_TRY - { - return set_parent(m_value.object->at(key)); - } - JSON_CATCH (std::out_of_range&) - { - // create better exception explanation - JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this)); - } + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); } - else + + auto it = m_value.object->find(key); + if (it == m_value.object->end()) + { + JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this)); + } + return set_parent(it->second); + } + + /// @brief access specified object element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + template < class KeyType, detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0 > + reference at(KeyType && key) + { + // at only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) { JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); } + + auto it = m_value.object->find(std::forward(key)); + if (it == m_value.object->end()) + { + // should key be forwarded here? (might call move ctor) + JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(key), "' not found"), this)); + } + return set_parent(it->second); } /// @brief access specified object element with bounds checking @@ -19569,22 +19585,37 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const_reference at(const typename object_t::key_type& key) const { // at only works for objects - if (JSON_HEDLEY_LIKELY(is_object())) + if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_TRY - { - return m_value.object->at(key); - } - JSON_CATCH (std::out_of_range&) - { - // create better exception explanation - JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this)); - } + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); } - else + + auto it = m_value.object->find(key); + if (it == m_value.object->end()) + { + JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(key), "' not found"), this)); + } + return it->second; + } + + /// @brief access specified object element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + template < class KeyType, detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0 > + const_reference at(KeyType && key) const + { + // at only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) { JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); } + + auto it = m_value.object->find(std::forward(key)); + if (it == m_value.object->end()) + { + JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(key), "' not found"), this)); + } + return it->second; } /// @brief access specified array element @@ -19648,7 +19679,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief access specified object element /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ - reference operator[](const typename object_t::key_type& key) + template < class KeyType, detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0 > + reference operator[](KeyType && key) { // implicitly convert null value to an empty object if (is_null()) @@ -19661,7 +19694,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - return set_parent(m_value.object->operator[](key)); + auto result = m_value.object->emplace(std::forward(key), nullptr); + return set_parent(result.first->second); } JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); @@ -19669,13 +19703,16 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief access specified object element /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ - const_reference operator[](const typename object_t::key_type& key) const + template < class KeyType, detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0 > + const_reference operator[](KeyType && key) const { // const operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); - return m_value.object->find(key)->second; + auto it = m_value.object->find(std::forward(key)); + JSON_ASSERT(it != m_value.object->end()); + return it->second; } JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); @@ -19695,7 +19732,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec assert_invariant(); } - // at only works for objects + // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { return set_parent(m_value.object->operator[](key)); @@ -19710,7 +19747,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_HEDLEY_NON_NULL(2) const_reference operator[](T* key) const { - // at only works for objects + // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); @@ -19723,22 +19760,23 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief access specified object element with default value /// @sa https://json.nlohmann.me/api/basic_json/value/ /// using std::is_convertible in a std::enable_if will fail when using explicit conversions - template < class ValueType, typename std::enable_if < + template < class KeyType, class ValueType, detail::enable_if_t < detail::is_getable::value - && !std::is_same::value, int >::type = 0 > - ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const + && !std::is_same::value + && !detail::is_json_pointer::value, int > = 0 > + typename std::decay::type value(KeyType && key, ValueType && default_value) const { - // at only works for objects + // value only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { // if key is found, return value and given default value otherwise - const auto it = find(key); + const auto it = find(std::forward(key)); if (it != end()) { - return it->template get(); + return it->template get::type>(); } - return default_value; + return std::forward(default_value); } JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); @@ -19747,18 +19785,20 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief access specified object element with default value /// @sa https://json.nlohmann.me/api/basic_json/value/ /// overload for a default value of type const char* - string_t value(const typename object_t::key_type& key, const char* default_value) const + template < class KeyType, detail::enable_if_t < + !detail::is_json_pointer::value, int > = 0 > + string_t value(KeyType && key, const char* default_value) const { - return value(key, string_t(default_value)); + return value(std::forward(key), string_t(default_value)); } /// @brief access specified object element via JSON Pointer with default value /// @sa https://json.nlohmann.me/api/basic_json/value/ - template::value, int>::type = 0> + template < class ValueType, detail::enable_if_t < + detail::is_getable::value, int> = 0 > ValueType value(const json_pointer& ptr, const ValueType& default_value) const { - // at only works for objects + // value only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { // if pointer resolves a value, return it or use default value @@ -19775,8 +19815,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); } - template::value, int>::type = 0> + template < class ValueType, class BasicJsonType, detail::enable_if_t < + detail::is_getable::value, int> = 0 > JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) ValueType value(const ::nlohmann::json_pointer& ptr, const ValueType& default_value) const { @@ -19834,10 +19874,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief remove element given an iterator /// @sa https://json.nlohmann.me/api/basic_json/erase/ - template < class IteratorType, typename std::enable_if < + template < class IteratorType, detail::enable_if_t < std::is_same::value || - std::is_same::value, int >::type - = 0 > + std::is_same::value, int > = 0 > IteratorType erase(IteratorType pos) { // make sure iterator fits the current value @@ -19905,10 +19944,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief remove elements given an iterator range /// @sa https://json.nlohmann.me/api/basic_json/erase/ - template < class IteratorType, typename std::enable_if < + template < class IteratorType, detail::enable_if_t < std::is_same::value || - std::is_same::value, int >::type - = 0 > + std::is_same::value, int > = 0 > IteratorType erase(IteratorType first, IteratorType last) { // make sure iterator fits the current value @@ -19977,17 +20015,57 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return result; } - /// @brief remove element from a JSON object given a key - /// @sa https://json.nlohmann.me/api/basic_json/erase/ - size_type erase(const typename object_t::key_type& key) + private: + template < typename KeyType, detail::enable_if_t < + detail::has_erase_with_key_type::value, int > = 0 > + size_type erase_internal(KeyType && key) { // this erase only works for objects - if (JSON_HEDLEY_LIKELY(is_object())) + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); + } + + return m_value.object->erase(std::forward(key)); + } + + template < typename KeyType, detail::enable_if_t < + !detail::has_erase_with_key_type::value, int > = 0 > + size_type erase_internal(KeyType && key) + { + // this erase only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) { - return m_value.object->erase(key); + 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_value.object->find(std::forward(key)); + if (it != m_value.object->end()) + { + m_value.object->erase(it); + return 1; + } + return 0; + } + + public: + + /// @brief remove element from a JSON object given a key + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + size_type erase(const typename object_t::key_type& key) + { + // the indirection via erase_internal() is needed to avoid making this + // function a template and thus de-rank it during overload resolution + return erase_internal(key); + } + + /// @brief remove element from a JSON object given a key + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + template < class KeyType, detail::enable_if_t < + detail::is_usable_as_key_type::value, int > = 0 > + size_type erase(KeyType && key) + { + return erase_internal(std::forward(key)); } /// @brief remove element from a JSON array given an index @@ -20022,14 +20100,41 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief find an element in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/find/ - template - iterator find(KeyT&& key) + iterator find(const typename object_t::key_type& key) { auto result = end(); if (is_object()) { - result.m_it.object_iterator = m_value.object->find(std::forward(key)); + result.m_it.object_iterator = m_value.object->find(key); + } + + return result; + } + + template < class KeyType, detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0> + iterator find(KeyType && key) + { + auto result = end(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(std::forward(key)); + } + + return result; + } + + /// @brief find an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/find/ + const_iterator find(const typename object_t::key_type& key) const + { + auto result = cend(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(key); } return result; @@ -20037,14 +20142,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief find an element in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/find/ - template - const_iterator find(KeyT&& key) const + template < class KeyType, detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0 > + const_iterator find(KeyType && key) const { auto result = cend(); if (is_object()) { - result.m_it.object_iterator = m_value.object->find(std::forward(key)); + result.m_it.object_iterator = m_value.object->find(std::forward(key)); } return result; @@ -20052,20 +20158,36 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief returns the number of occurrences of a key in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/count/ - template - size_type count(KeyT&& key) const + size_type count(const typename object_t::key_type& key) const { // return 0 for all nonobject types - return is_object() ? m_value.object->count(std::forward(key)) : 0; + return is_object() ? m_value.object->count(key) : 0; + } + + /// @brief returns the number of occurrences of a key in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/count/ + template < class KeyType, detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0 > + size_type count(KeyType && key) const + { + // return 0 for all nonobject types + return is_object() ? m_value.object->count(std::forward(key)) : 0; + } + + /// @brief check the existence of an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/contains/ + bool contains(const typename object_t::key_type& key) const + { + return is_object() && m_value.object->find(key) != m_value.object->end(); } /// @brief check the existence of an element in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/contains/ - template < typename KeyT, typename std::enable_if < - !detail::is_json_pointer::type>::value, int >::type = 0 > - bool contains(KeyT && key) const + template < class KeyType, detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0 > + bool contains(KeyType && key) const { - return is_object() && m_value.object->find(std::forward(key)) != m_value.object->end(); + return is_object() && m_value.object->find(std::forward(key)) != m_value.object->end(); } /// @brief check the existence of an element in a JSON object given a JSON pointer diff --git a/test/src/unit-element_access2.cpp b/test/src/unit-element_access2.cpp index 94e0df42d2..11daba1455 100644 --- a/test/src/unit-element_access2.cpp +++ b/test/src/unit-element_access2.cpp @@ -60,12 +60,38 @@ TEST_CASE("element access 2") CHECK(j_const.at("floating") == json(42.23)); CHECK(j_const.at("object") == json::object()); CHECK(j_const.at("array") == json({1, 2, 3})); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.at(std::string_view("integer")) == json(1)); + CHECK(j.at(std::string_view("unsigned")) == json(1u)); + CHECK(j.at(std::string_view("boolean")) == json(true)); + CHECK(j.at(std::string_view("null")) == json(nullptr)); + CHECK(j.at(std::string_view("string")) == json("hello world")); + CHECK(j.at(std::string_view("floating")) == json(42.23)); + CHECK(j.at(std::string_view("object")) == json::object()); + CHECK(j.at(std::string_view("array")) == json({1, 2, 3})); + + CHECK(j_const.at(std::string_view("integer")) == json(1)); + CHECK(j_const.at(std::string_view("unsigned")) == json(1u)); + CHECK(j_const.at(std::string_view("boolean")) == json(true)); + CHECK(j_const.at(std::string_view("null")) == json(nullptr)); + CHECK(j_const.at(std::string_view("string")) == json("hello world")); + CHECK(j_const.at(std::string_view("floating")) == json(42.23)); + CHECK(j_const.at(std::string_view("object")) == json::object()); + CHECK(j_const.at(std::string_view("array")) == json({1, 2, 3})); +#endif } SECTION("access outside bounds") { CHECK_THROWS_WITH_AS(j.at("foo"), "[json.exception.out_of_range.403] key 'foo' not found", json::out_of_range&); CHECK_THROWS_WITH_AS(j_const.at("foo"), "[json.exception.out_of_range.403] key 'foo' not found", json::out_of_range&); + + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j.at(std::string_view("foo")), "[json.exception.out_of_range.403] key 'foo' not found", json::out_of_range&); + CHECK_THROWS_WITH_AS(j_const.at(std::string_view("foo")), "[json.exception.out_of_range.403] key 'foo' not found", json::out_of_range&); +#endif } SECTION("access on non-object type") @@ -76,6 +102,11 @@ TEST_CASE("element access 2") const json j_nonobject_const(j_nonobject); CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with null", json::type_error&); CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with null", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject.at(std::string_view(std::string_view("foo"))), "[json.exception.type_error.304] cannot use at() with null", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.at(std::string_view(std::string_view("foo"))), "[json.exception.type_error.304] cannot use at() with null", json::type_error&); +#endif } SECTION("boolean") @@ -84,6 +115,11 @@ TEST_CASE("element access 2") const json j_nonobject_const(j_nonobject); CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with boolean", json::type_error&); CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with boolean", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with boolean", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with boolean", json::type_error&); +#endif } SECTION("string") @@ -92,6 +128,11 @@ TEST_CASE("element access 2") const json j_nonobject_const(j_nonobject); CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with string", json::type_error&); CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with string", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with string", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with string", json::type_error&); +#endif } SECTION("array") @@ -100,6 +141,11 @@ TEST_CASE("element access 2") const json j_nonobject_const(j_nonobject); CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with array", json::type_error&); CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with array", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with array", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with array", json::type_error&); +#endif } SECTION("number (integer)") @@ -108,6 +154,11 @@ TEST_CASE("element access 2") const json j_nonobject_const(j_nonobject); CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); +#endif } SECTION("number (unsigned)") @@ -116,6 +167,11 @@ TEST_CASE("element access 2") const json j_nonobject_const(j_nonobject); CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); +#endif } SECTION("number (floating-point)") @@ -124,6 +180,11 @@ TEST_CASE("element access 2") const json j_nonobject_const(j_nonobject); CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); +#endif } } } @@ -158,6 +219,33 @@ TEST_CASE("element access 2") CHECK(j_const.value("floating", 12) == 42); CHECK(j_const.value("object", json({{"foo", "bar"}})) == json::object()); CHECK(j_const.value("array", json({10, 100})) == json({1, 2, 3})); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.value(std::string_view("integer"), 2) == 1); + CHECK(j.value(std::string_view("integer"), 1.0) == Approx(1)); + CHECK(j.value(std::string_view("unsigned"), 2) == 1u); + CHECK(j.value(std::string_view("unsigned"), 1.0) == Approx(1u)); + CHECK(j.value(std::string_view("null"), json(1)) == json()); + CHECK(j.value(std::string_view("boolean"), false) == true); + CHECK(j.value(std::string_view("string"), "bar") == "hello world"); + CHECK(j.value(std::string_view("string"), std::string("bar")) == "hello world"); + CHECK(j.value(std::string_view("floating"), 12.34) == Approx(42.23)); + CHECK(j.value(std::string_view("floating"), 12) == 42); + CHECK(j.value(std::string_view("object"), json({{"foo", "bar"}})) == json::object()); + CHECK(j.value(std::string_view("array"), json({10, 100})) == json({1, 2, 3})); + + CHECK(j_const.value(std::string_view("integer"), 2) == 1); + CHECK(j_const.value(std::string_view("integer"), 1.0) == Approx(1)); + CHECK(j_const.value(std::string_view("unsigned"), 2) == 1u); + CHECK(j_const.value(std::string_view("unsigned"), 1.0) == Approx(1u)); + CHECK(j_const.value(std::string_view("boolean"), false) == true); + CHECK(j_const.value(std::string_view("string"), "bar") == "hello world"); + CHECK(j_const.value(std::string_view("string"), std::string("bar")) == "hello world"); + CHECK(j_const.value(std::string_view("floating"), 12.34) == Approx(42.23)); + CHECK(j_const.value(std::string_view("floating"), 12) == 42); + CHECK(j_const.value(std::string_view("object"), json({{"foo", "bar"}})) == json::object()); + CHECK(j_const.value(std::string_view("array"), json({10, 100})) == json({1, 2, 3})); +#endif } SECTION("access non-existing value") @@ -177,6 +265,24 @@ TEST_CASE("element access 2") CHECK(j_const.value("_", 12.34) == Approx(12.34)); CHECK(j_const.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}})); CHECK(j_const.value("_", json({10, 100})) == json({10, 100})); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.value(std::string_view("_"), 2) == 2); + CHECK(j.value(std::string_view("_"), 2u) == 2u); + CHECK(j.value(std::string_view("_"), false) == false); + CHECK(j.value(std::string_view("_"), "bar") == "bar"); + CHECK(j.value(std::string_view("_"), 12.34) == Approx(12.34)); + CHECK(j.value(std::string_view("_"), json({{"foo", "bar"}})) == json({{"foo", "bar"}})); + CHECK(j.value(std::string_view("_"), json({10, 100})) == json({10, 100})); + + CHECK(j_const.value(std::string_view("_"), 2) == 2); + CHECK(j_const.value(std::string_view("_"), 2u) == 2u); + CHECK(j_const.value(std::string_view("_"), false) == false); + CHECK(j_const.value(std::string_view("_"), "bar") == "bar"); + CHECK(j_const.value(std::string_view("_"), 12.34) == Approx(12.34)); + CHECK(j_const.value(std::string_view("_"), json({{"foo", "bar"}})) == json({{"foo", "bar"}})); + CHECK(j_const.value(std::string_view("_"), json({10, 100})) == json({10, 100})); +#endif } SECTION("access on non-object type") @@ -187,6 +293,11 @@ TEST_CASE("element access 2") const json j_nonobject_const(json::value_t::null); CHECK_THROWS_WITH_AS(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with null", json::type_error&); CHECK_THROWS_WITH_AS(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with null", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with null", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with null", json::type_error&); +#endif } SECTION("boolean") @@ -195,6 +306,11 @@ TEST_CASE("element access 2") const json j_nonobject_const(json::value_t::boolean); CHECK_THROWS_WITH_AS(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with boolean", json::type_error&); CHECK_THROWS_WITH_AS(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with boolean", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with boolean", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with boolean", json::type_error&); +#endif } SECTION("string") @@ -203,6 +319,11 @@ TEST_CASE("element access 2") const json j_nonobject_const(json::value_t::string); CHECK_THROWS_WITH_AS(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with string", json::type_error&); CHECK_THROWS_WITH_AS(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with string", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with string", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with string", json::type_error&); +#endif } SECTION("array") @@ -211,6 +332,11 @@ TEST_CASE("element access 2") const json j_nonobject_const(json::value_t::array); CHECK_THROWS_WITH_AS(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with array", json::type_error&); CHECK_THROWS_WITH_AS(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with array", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with array", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with array", json::type_error&); +#endif } SECTION("number (integer)") @@ -219,6 +345,11 @@ TEST_CASE("element access 2") const json j_nonobject_const(json::value_t::number_integer); CHECK_THROWS_WITH_AS(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); CHECK_THROWS_WITH_AS(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); +#endif } SECTION("number (unsigned)") @@ -227,6 +358,11 @@ TEST_CASE("element access 2") const json j_nonobject_const(json::value_t::number_unsigned); CHECK_THROWS_WITH_AS(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); CHECK_THROWS_WITH_AS(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); +#endif } SECTION("number (floating-point)") @@ -235,6 +371,11 @@ TEST_CASE("element access 2") const json j_nonobject_const(json::value_t::number_float); CHECK_THROWS_WITH_AS(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); CHECK_THROWS_WITH_AS(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.value(std::string_view("foo"), 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); +#endif } } } @@ -330,6 +471,31 @@ TEST_CASE("element access 2") } } + SECTION("non-const operator[]") + { + { + json j_null; + CHECK(j_null.is_null()); + j_null["key"] = 1; + CHECK(j_null.is_object()); + CHECK(j_null.size() == 1); + j_null["key"] = 2; + CHECK(j_null.size() == 1); + } +#ifdef JSON_HAS_CPP_17 + { + std::string_view key = "key"; + json j_null; + CHECK(j_null.is_null()); + j_null[key] = 1; + CHECK(j_null.is_object()); + CHECK(j_null.size() == 1); + j_null[key] = 2; + CHECK(j_null.size() == 1); + } +#endif + } + SECTION("front and back") { // "array" is the smallest key @@ -390,6 +556,56 @@ TEST_CASE("element access 2") CHECK(j_const[json::object_t::key_type("array")] == j["array"]); } +#ifdef JSON_HAS_CPP_17 + SECTION("access within bounds (string_view)") + { + CHECK(j["integer"] == json(1)); + CHECK(j[std::string_view("integer")] == j["integer"]); + + CHECK(j["unsigned"] == json(1u)); + CHECK(j[std::string_view("unsigned")] == j["unsigned"]); + + CHECK(j["boolean"] == json(true)); + CHECK(j[std::string_view("boolean")] == j["boolean"]); + + CHECK(j["null"] == json(nullptr)); + CHECK(j[std::string_view("null")] == j["null"]); + + CHECK(j["string"] == json("hello world")); + CHECK(j[std::string_view("string")] == j["string"]); + + CHECK(j["floating"] == json(42.23)); + CHECK(j[std::string_view("floating")] == j["floating"]); + + CHECK(j["object"] == json::object()); + CHECK(j[std::string_view("object")] == j["object"]); + + CHECK(j["array"] == json({1, 2, 3})); + CHECK(j[std::string_view("array")] == j["array"]); + + CHECK(j_const["integer"] == json(1)); + CHECK(j_const[std::string_view("integer")] == j["integer"]); + + CHECK(j_const["boolean"] == json(true)); + CHECK(j_const[std::string_view("boolean")] == j["boolean"]); + + CHECK(j_const["null"] == json(nullptr)); + CHECK(j_const[std::string_view("null")] == j["null"]); + + CHECK(j_const["string"] == json("hello world")); + CHECK(j_const[std::string_view("string")] == j["string"]); + + CHECK(j_const["floating"] == json(42.23)); + CHECK(j_const[std::string_view("floating")] == j["floating"]); + + CHECK(j_const["object"] == json::object()); + CHECK(j_const[std::string_view("object")] == j["object"]); + + CHECK(j_const["array"] == json({1, 2, 3})); + CHECK(j_const[std::string_view("array")] == j["array"]); + } +#endif + SECTION("access on non-object type") { SECTION("null") @@ -397,10 +613,16 @@ TEST_CASE("element access 2") json j_nonobject(json::value_t::null); json j_nonobject2(json::value_t::null); const json j_const_nonobject(j_nonobject); + CHECK_NOTHROW(j_nonobject["foo"]); CHECK_NOTHROW(j_nonobject2[json::object_t::key_type("foo")]); CHECK_THROWS_WITH_AS(j_const_nonobject["foo"], "[json.exception.type_error.305] cannot use operator[] with a string argument with null", json::type_error&); CHECK_THROWS_WITH_AS(j_const_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with null", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_NOTHROW(j_nonobject2[std::string_view("foo")]); + CHECK_THROWS_WITH_AS(j_const_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with null", json::type_error&); +#endif } SECTION("boolean") @@ -415,6 +637,11 @@ TEST_CASE("element access 2") "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean", json::type_error&); CHECK_THROWS_WITH_AS(j_const_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean", json::type_error&); + CHECK_THROWS_WITH_AS(j_const_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean", json::type_error&); +#endif } SECTION("string") @@ -429,6 +656,11 @@ TEST_CASE("element access 2") "[json.exception.type_error.305] cannot use operator[] with a string argument with string", json::type_error&); CHECK_THROWS_WITH_AS(j_const_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with string", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with string", json::type_error&); + CHECK_THROWS_WITH_AS(j_const_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with string", json::type_error&); +#endif } SECTION("array") @@ -442,6 +674,11 @@ TEST_CASE("element access 2") "[json.exception.type_error.305] cannot use operator[] with a string argument with array", json::type_error&); CHECK_THROWS_WITH_AS(j_const_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with array", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with array", json::type_error&); + CHECK_THROWS_WITH_AS(j_const_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with array", json::type_error&); +#endif } SECTION("number (integer)") @@ -456,6 +693,11 @@ TEST_CASE("element access 2") "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); CHECK_THROWS_WITH_AS(j_const_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_const_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); +#endif } SECTION("number (unsigned)") @@ -470,6 +712,11 @@ TEST_CASE("element access 2") "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); CHECK_THROWS_WITH_AS(j_const_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_const_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); +#endif } SECTION("number (floating-point)") @@ -484,6 +731,11 @@ TEST_CASE("element access 2") "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); CHECK_THROWS_WITH_AS(j_const_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_const_nonobject[std::string_view("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); +#endif } } } @@ -533,6 +785,51 @@ TEST_CASE("element access 2") CHECK(j.erase("array") == 0); } +#ifdef JSON_HAS_CPP_17 + SECTION("remove element by key (string_view)") + { + CHECK(j.find(std::string_view("integer")) != j.end()); + CHECK(j.erase(std::string_view("integer")) == 1); + CHECK(j.find(std::string_view("integer")) == j.end()); + CHECK(j.erase(std::string_view("integer")) == 0); + + CHECK(j.find(std::string_view("unsigned")) != j.end()); + CHECK(j.erase(std::string_view("unsigned")) == 1); + CHECK(j.find(std::string_view("unsigned")) == j.end()); + CHECK(j.erase(std::string_view("unsigned")) == 0); + + CHECK(j.find(std::string_view("boolean")) != j.end()); + CHECK(j.erase(std::string_view("boolean")) == 1); + CHECK(j.find(std::string_view("boolean")) == j.end()); + CHECK(j.erase(std::string_view("boolean")) == 0); + + CHECK(j.find(std::string_view("null")) != j.end()); + CHECK(j.erase(std::string_view("null")) == 1); + CHECK(j.find(std::string_view("null")) == j.end()); + CHECK(j.erase(std::string_view("null")) == 0); + + CHECK(j.find(std::string_view("string")) != j.end()); + CHECK(j.erase(std::string_view("string")) == 1); + CHECK(j.find(std::string_view("string")) == j.end()); + CHECK(j.erase(std::string_view("string")) == 0); + + CHECK(j.find(std::string_view("floating")) != j.end()); + CHECK(j.erase(std::string_view("floating")) == 1); + CHECK(j.find(std::string_view("floating")) == j.end()); + CHECK(j.erase(std::string_view("floating")) == 0); + + CHECK(j.find(std::string_view("object")) != j.end()); + CHECK(j.erase(std::string_view("object")) == 1); + CHECK(j.find(std::string_view("object")) == j.end()); + CHECK(j.erase(std::string_view("object")) == 0); + + CHECK(j.find(std::string_view("array")) != j.end()); + CHECK(j.erase(std::string_view("array")) == 1); + CHECK(j.find(std::string_view("array")) == j.end()); + CHECK(j.erase(std::string_view("array")) == 0); + } +#endif + SECTION("remove element by iterator") { SECTION("erase(begin())") @@ -652,36 +949,60 @@ TEST_CASE("element access 2") { json j_nonobject(json::value_t::null); CHECK_THROWS_WITH_AS(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with null", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject.erase(std::string_view("foo")), "[json.exception.type_error.307] cannot use erase() with null", json::type_error&); +#endif } SECTION("boolean") { json j_nonobject(json::value_t::boolean); CHECK_THROWS_WITH_AS(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with boolean", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject.erase(std::string_view("foo")), "[json.exception.type_error.307] cannot use erase() with boolean", json::type_error&); +#endif } SECTION("string") { json j_nonobject(json::value_t::string); CHECK_THROWS_WITH_AS(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with string", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject.erase(std::string_view("foo")), "[json.exception.type_error.307] cannot use erase() with string", json::type_error&); +#endif } SECTION("array") { json j_nonobject(json::value_t::array); CHECK_THROWS_WITH_AS(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with array", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject.erase(std::string_view("foo")), "[json.exception.type_error.307] cannot use erase() with array", json::type_error&); +#endif } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); CHECK_THROWS_WITH_AS(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with number", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject.erase(std::string_view("foo")), "[json.exception.type_error.307] cannot use erase() with number", json::type_error&); +#endif } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); CHECK_THROWS_WITH_AS(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with number", json::type_error&); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_WITH_AS(j_nonobject.erase(std::string_view("foo")), "[json.exception.type_error.307] cannot use erase() with number", json::type_error&); +#endif } } } @@ -699,12 +1020,28 @@ TEST_CASE("element access 2") CHECK(j_const.find(key) != j_const.end()); CHECK(*j_const.find(key) == j_const.at(key)); } +#ifdef JSON_HAS_CPP_17 + for (const std::string_view key : + {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" + }) + { + CHECK(j.find(key) != j.end()); + CHECK(*j.find(key) == j.at(key)); + CHECK(j_const.find(key) != j_const.end()); + CHECK(*j_const.find(key) == j_const.at(key)); + } +#endif } SECTION("nonexisting element") { CHECK(j.find("foo") == j.end()); CHECK(j_const.find("foo") == j_const.end()); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.find(std::string_view("foo")) == j.end()); + CHECK(j_const.find(std::string_view("foo")) == j_const.end()); +#endif } SECTION("all types") @@ -713,64 +1050,112 @@ TEST_CASE("element access 2") { json j_nonarray(json::value_t::null); const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); + CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); +#endif } SECTION("string") { json j_nonarray(json::value_t::string); const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); + CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); +#endif } SECTION("object") { json j_nonarray(json::value_t::object); const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); + CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); +#endif } SECTION("array") { json j_nonarray(json::value_t::array); const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); + CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); +#endif } SECTION("boolean") { json j_nonarray(json::value_t::boolean); const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); + CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); +#endif } SECTION("number (integer)") { json j_nonarray(json::value_t::number_integer); const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); + CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); +#endif } SECTION("number (unsigned)") { json j_nonarray(json::value_t::number_unsigned); const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); + CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); +#endif } SECTION("number (floating-point)") { json j_nonarray(json::value_t::number_float); const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); + CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); +#endif } } } @@ -786,12 +1171,26 @@ TEST_CASE("element access 2") CHECK(j.count(key) == 1); CHECK(j_const.count(key) == 1); } +#ifdef JSON_HAS_CPP_17 + for (const std::string_view key : + {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" + }) + { + CHECK(j.count(key) == 1); + CHECK(j_const.count(key) == 1); + } +#endif } SECTION("nonexisting element") { CHECK(j.count("foo") == 0); CHECK(j_const.count("foo") == 0); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.count(std::string_view("foo")) == 0); + CHECK(j_const.count(std::string_view("foo")) == 0); +#endif } SECTION("all types") @@ -800,64 +1199,112 @@ TEST_CASE("element access 2") { json j_nonobject(json::value_t::null); const json j_nonobject_const(json::value_t::null); + CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.count(std::string_view("foo")) == 0); + CHECK(j_const.count(std::string_view("foo")) == 0); +#endif } SECTION("string") { json j_nonobject(json::value_t::string); const json j_nonobject_const(json::value_t::string); + CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.count(std::string_view("foo")) == 0); + CHECK(j_const.count(std::string_view("foo")) == 0); +#endif } SECTION("object") { json j_nonobject(json::value_t::object); const json j_nonobject_const(json::value_t::object); + CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.count(std::string_view("foo")) == 0); + CHECK(j_const.count(std::string_view("foo")) == 0); +#endif } SECTION("array") { json j_nonobject(json::value_t::array); const json j_nonobject_const(json::value_t::array); + CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.count(std::string_view("foo")) == 0); + CHECK(j_const.count(std::string_view("foo")) == 0); +#endif } SECTION("boolean") { json j_nonobject(json::value_t::boolean); const json j_nonobject_const(json::value_t::boolean); + CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.count(std::string_view("foo")) == 0); + CHECK(j_const.count(std::string_view("foo")) == 0); +#endif } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); const json j_nonobject_const(json::value_t::number_integer); + CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.count(std::string_view("foo")) == 0); + CHECK(j_const.count(std::string_view("foo")) == 0); +#endif } SECTION("number (unsigned)") { json j_nonobject(json::value_t::number_unsigned); const json j_nonobject_const(json::value_t::number_unsigned); + CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.count(std::string_view("foo")) == 0); + CHECK(j_const.count(std::string_view("foo")) == 0); +#endif } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); const json j_nonobject_const(json::value_t::number_float); + CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.count(std::string_view("foo")) == 0); + CHECK(j_const.count(std::string_view("foo")) == 0); +#endif } } } @@ -873,12 +1320,27 @@ TEST_CASE("element access 2") CHECK(j.contains(key) == true); CHECK(j_const.contains(key) == true); } + +#ifdef JSON_HAS_CPP_17 + for (const std::string_view key : + {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" + }) + { + CHECK(j.contains(key) == true); + CHECK(j_const.contains(key) == true); + } +#endif } SECTION("nonexisting element") { CHECK(j.contains("foo") == false); CHECK(j_const.contains("foo") == false); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.contains(std::string_view("foo")) == false); + CHECK(j_const.contains(std::string_view("foo")) == false); +#endif } SECTION("all types") @@ -887,56 +1349,98 @@ TEST_CASE("element access 2") { json j_nonobject(json::value_t::null); const json j_nonobject_const(json::value_t::null); + CHECK(j_nonobject.contains("foo") == false); CHECK(j_nonobject_const.contains("foo") == false); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonobject.contains(std::string_view("foo")) == false); + CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); +#endif } SECTION("string") { json j_nonobject(json::value_t::string); const json j_nonobject_const(json::value_t::string); + CHECK(j_nonobject.contains("foo") == false); CHECK(j_nonobject_const.contains("foo") == false); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonobject.contains(std::string_view("foo")) == false); + CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); +#endif } SECTION("object") { json j_nonobject(json::value_t::object); const json j_nonobject_const(json::value_t::object); + CHECK(j_nonobject.contains("foo") == false); CHECK(j_nonobject_const.contains("foo") == false); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonobject.contains(std::string_view("foo")) == false); + CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); +#endif } SECTION("array") { json j_nonobject(json::value_t::array); const json j_nonobject_const(json::value_t::array); + CHECK(j_nonobject.contains("foo") == false); CHECK(j_nonobject_const.contains("foo") == false); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonobject.contains(std::string_view("foo")) == false); + CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); +#endif } SECTION("boolean") { json j_nonobject(json::value_t::boolean); const json j_nonobject_const(json::value_t::boolean); + CHECK(j_nonobject.contains("foo") == false); CHECK(j_nonobject_const.contains("foo") == false); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonobject.contains(std::string_view("foo")) == false); + CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); +#endif } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); const json j_nonobject_const(json::value_t::number_integer); + CHECK(j_nonobject.contains("foo") == false); CHECK(j_nonobject_const.contains("foo") == false); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonobject.contains(std::string_view("foo")) == false); + CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); +#endif } SECTION("number (unsigned)") { json j_nonobject(json::value_t::number_unsigned); const json j_nonobject_const(json::value_t::number_unsigned); + CHECK(j_nonobject.contains("foo") == false); CHECK(j_nonobject_const.contains("foo") == false); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonobject.contains(std::string_view("foo")) == false); + CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); +#endif } SECTION("number (floating-point)") @@ -945,6 +1449,10 @@ TEST_CASE("element access 2") const json j_nonobject_const(json::value_t::number_float); CHECK(j_nonobject.contains("foo") == false); CHECK(j_nonobject_const.contains("foo") == false); +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonobject.contains(std::string_view("foo")) == false); + CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); +#endif } } }