From 4e60bce9c2291d016e1c21687bd10a3ca72b0585 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Thu, 3 Mar 2022 14:23:13 +0100 Subject: [PATCH] Rework basic_json element access to accept more key types Rework basic_json element access member functions and operators to accept any type that meets the requirements defined by type trait detail::is_usable_as_key_type. Member functions and operators: * at() * operator[] * value() * erase() * find() * count() * contains() Update documentation to reflect these changes. Add unit tests to excercise the new functions using std::string_view. Co-authored-by: Niels Lohmann --- doc/mkdocs/docs/api/basic_json/at.md | 41 +- doc/mkdocs/docs/api/basic_json/basic_json.md | 10 +- doc/mkdocs/docs/api/basic_json/contains.md | 32 +- doc/mkdocs/docs/api/basic_json/count.md | 23 +- .../basic_json/default_object_comparator_t.md | 3 +- doc/mkdocs/docs/api/basic_json/erase.md | 39 +- doc/mkdocs/docs/api/basic_json/find.md | 33 +- doc/mkdocs/docs/api/basic_json/insert.md | 2 +- doc/mkdocs/docs/api/basic_json/operator[].md | 52 +- doc/mkdocs/docs/api/basic_json/to_bson.md | 2 +- doc/mkdocs/docs/api/basic_json/to_cbor.md | 2 +- doc/mkdocs/docs/api/basic_json/to_msgpack.md | 2 +- doc/mkdocs/docs/api/basic_json/to_ubjson.md | 2 +- doc/mkdocs/docs/api/basic_json/value.md | 32 +- include/nlohmann/json.hpp | 339 +++++++++--- single_include/nlohmann/json.hpp | 339 +++++++++--- test/src/unit-element_access2.cpp | 508 ++++++++++++++++++ 17 files changed, 1214 insertions(+), 247 deletions(-) diff --git a/doc/mkdocs/docs/api/basic_json/at.md b/doc/mkdocs/docs/api/basic_json/at.md index fc518f46e4..dbd4cb6c96 100644 --- a/doc/mkdocs/docs/api/basic_json/at.md +++ b/doc/mkdocs/docs/api/basic_json/at.md @@ -10,13 +10,28 @@ reference at(const typename object_t::key_type& key); const_reference at(const typename object_t::key_type& key) const; // (3) +template +reference at(KeyType&& key); +template +const_reference at(KeyType&& key) const; + +// (4) reference at(const json_pointer& ptr); const_reference at(const json_pointer& ptr) const; ``` 1. Returns a reference to the array element at specified location `idx`, with bounds checking. -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. +2. Returns a reference to the object element with specified key `key`, with bounds checking. +3. See 2. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and + `#!cpp typename object_comparator_t::is_transparent` denotes a type. +4. Returns a reference to the element at 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 view (C++17). ## Parameters @@ -25,15 +40,16 @@ const_reference at(const json_pointer& ptr) const; `key` (in) : object key of the elements to access - + `ptr` (in) : JSON pointer to the desired element - + ## Return value 1. reference to the element at index `idx` 2. reference to the element at key `key` -3. reference to the element pointed to by `ptr` +3. reference to the element at key `key` +4. reference to the element pointed to by `ptr` ## Exception safety @@ -51,7 +67,8 @@ Strong exception safety: if an exception occurs, the original value stays intact in this case, calling `at` with a key makes no sense. See example below. - Throws [`out_of_range.403`](../../home/exceptions.md#jsonexceptionout_of_range403) if the key `key` is not stored in the object; that is, `find(key) == end()`. See example below. -3. The function can throw the following exceptions: +3. See 2. +4. The function can throw the following exceptions: - Throws [`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106) if an array index in the passed JSON pointer `ptr` begins with '0'. See example below. - Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if an array index in the passed @@ -68,9 +85,10 @@ Strong exception safety: if an exception occurs, the original value stays intact ## Complexity -1. Constant +1. Constant. 2. Logarithmic in the size of the container. -3. Constant +3. Logarithmic in the size of the container. +4. Logarithmic in the size of the container. ## Examples @@ -134,7 +152,7 @@ Strong exception safety: if an exception occurs, the original value stays intact --8<-- "examples/at__object_t_key_type_const.output" ``` -??? example "Example (3) access specified element via JSON Pointer" +??? example "Example (4) access specified element via JSON Pointer" The example below shows how object elements can be read and written using `at()`. It also demonstrates the different exceptions that can be thrown. @@ -149,7 +167,7 @@ Strong exception safety: if an exception occurs, the original value stays intact --8<-- "examples/at_json_pointer.output" ``` -??? example "Example (3) access specified element via JSON Pointer" +??? example "Example (4) access specified element via JSON Pointer" The example below shows how object elements can be read using `at()`. It also demonstrates the different exceptions that can be thrown. @@ -173,4 +191,5 @@ Strong exception safety: if an exception occurs, the original value stays intact 1. Added in version 1.0.0. 2. Added in version 1.0.0. -3. Added in version 2.0.0. +3. Added in version 3.11.0. +4. Added in version 2.0.0. diff --git a/doc/mkdocs/docs/api/basic_json/basic_json.md b/doc/mkdocs/docs/api/basic_json/basic_json.md index afa3901d1e..9a289d6e98 100644 --- a/doc/mkdocs/docs/api/basic_json/basic_json.md +++ b/doc/mkdocs/docs/api/basic_json/basic_json.md @@ -201,16 +201,16 @@ basic_json(basic_json&& other) noexcept; ## Exceptions -1. / +1. (none) 2. The function does not throw exceptions. -3. / -4. / +3. (none) +4. (none) 5. The function can throw the following exceptions: - Throws [`type_error.301`](../../home/exceptions.md#jsonexceptiontype_error301) if `type_deduction` is `#!cpp false`, `manual_type` is `value_t::object`, but `init` contains an element which is not a pair whose first element is a string. In this case, the constructor could not create an object. If `type_deduction` would have been `#!cpp true`, an array would have been created. See `object(initializer_list_t)` for an example. -6. / +6. (none) 7. The function can throw the following exceptions: - Throws [`invalid_iterator.201`](../../home/exceptions.md#jsonexceptioninvalid_iterator201) if iterators `first` and `last` are not compatible (i.e., do not belong to the same JSON value). In this case, the range @@ -220,7 +220,7 @@ basic_json(basic_json&& other) noexcept; element anymore. In this case, the range `[first, last)` is undefined. See example code below. - Throws [`invalid_iterator.206`](../../home/exceptions.md#jsonexceptioninvalid_iterator206) if iterators `first` and `last` belong to a `#!json null` value. In this case, the range `[first, last)` is undefined. -8. / +8. (none) 9. The function does not throw exceptions. ## Complexity diff --git a/doc/mkdocs/docs/api/basic_json/contains.md b/doc/mkdocs/docs/api/basic_json/contains.md index 8463f4ea98..67a5ffcc08 100644 --- a/doc/mkdocs/docs/api/basic_json/contains.md +++ b/doc/mkdocs/docs/api/basic_json/contains.md @@ -2,21 +2,28 @@ ```cpp // (1) -template -bool contains(KeyT && key) const; +bool contains(const typename object_t::key_type& key) const; // (2) +template +bool contains(KeyType&& key) const; + +// (3) bool contains(const json_pointer& ptr) const; ``` -1. Check whether an element exists in a JSON object with key equivalent to `key`. If the element is not found or the +1. Check whether an element exists in a JSON object with a key equivalent to `key`. If the element is not found or the JSON value is not an object, `#!cpp false` is returned. -2. Check whether the given JSON pointer `ptr` can be resolved in the current JSON value. +2. See 1. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and + `#!cpp typename object_comparator_t::is_transparent` denotes a type. +3. Check whether the given JSON pointer `ptr` can be resolved in the current JSON value. ## 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 view (C++17). ## Parameters @@ -30,7 +37,8 @@ bool contains(const json_pointer& ptr) const; 1. `#!cpp true` if an element with specified `key` exists. If no such element with such key is found or the JSON value is not an object, `#!cpp false` is returned. -2. `#!cpp true` if the JSON pointer can be resolved to a stored value, `#!cpp false` otherwise. +2. See 1. +3. `#!cpp true` if the JSON pointer can be resolved to a stored value, `#!cpp false` otherwise. ## Exception safety @@ -39,7 +47,8 @@ Strong exception safety: if an exception occurs, the original value stays intact ## Exceptions 1. The function does not throw exceptions. -2. The function can throw the following exceptions: +2. The function does not throw exceptions. +3. The function can throw the following exceptions: - Throws [`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106) if an array index begins with `0`. - Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if an array index was not a @@ -74,7 +83,7 @@ Logarithmic in the size of the JSON object. --8<-- "examples/contains.output" ``` -??? example "Example (1) check with JSON pointer" +??? example "Example (3) check with JSON pointer" The example shows how `contains()` is used. @@ -90,5 +99,6 @@ Logarithmic in the size of the JSON object. ## Version history -1. Added in version 3.6.0. -2. Added in version 3.7.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. +3. 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..de135be21d 100644 --- a/doc/mkdocs/docs/api/basic_json/count.md +++ b/doc/mkdocs/docs/api/basic_json/count.md @@ -1,17 +1,25 @@ # nlohmann::basic_json::count ```cpp -template -size_type count(KeyT&& key) const; +// (1) +size_type count(const typename object_t::key_type& key) const; + +// (2) +template +size_type count(KeyType&& key) const; ``` -Returns the number of elements with key `key`. If `ObjectType` is the default `std::map` type, the return value will -always be `0` (`key` was not found) or `1` (`key` was found). +1. Returns the number of elements with key `key`. If `ObjectType` is the default `std::map` type, the return value will + always be `0` (`key` was not found) or `1` (`key` was found). +2. See 1. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and + `#!cpp typename object_comparator_t::is_transparent` denotes a type. ## 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 view (C++17). ## Parameters @@ -52,4 +60,5 @@ This method always returns `0` when executed on a JSON type that is not an objec ## Version history -- Added in version 1.0.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. 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 265a9d385f..9e5f6c5bd0 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 @@ -8,7 +8,8 @@ using default_object_comparator_t = std::less<>; // since C++14 The default comparator used by [`object_t`](object_t.md). -Since C++14 a transparent comparator is used which prevents unnecessary string construction. +Since C++14 a transparent comparator is used which prevents unnecessary string construction +when looking up a key in an object. The actual comparator used depends on [`object_t`](object_t.md) and can be obtained via [`object_comparator_t`](object_comparator_t.md). diff --git a/doc/mkdocs/docs/api/basic_json/erase.md b/doc/mkdocs/docs/api/basic_json/erase.md index d94c25b7fd..6cb749b8c9 100644 --- a/doc/mkdocs/docs/api/basic_json/erase.md +++ b/doc/mkdocs/docs/api/basic_json/erase.md @@ -13,6 +13,10 @@ const_iterator erase(const_iterator first, const_iterator last); size_type erase(const typename object_t::key_type& key); // (4) +template +size_type erase(KeyType&& key); + +// (5) void erase(const size_type idx); ``` @@ -29,7 +33,17 @@ void erase(const size_type idx); 3. Removes an element from a JSON object by key. -4. Removes an element from a JSON array by index. +4. See 3. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and + `#!cpp typename object_comparator_t::is_transparent` denotes a type. + +5. 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 view (C++17). ## Parameters @@ -56,7 +70,8 @@ void erase(const size_type idx); is returned. 3. Number of elements removed. If `ObjectType` is the default `std::map` type, the return value will always be `0` (`key` was not found) or `1` (`key` was found). -4. / +4. See 3. +5. (none) ## Exception safety @@ -83,7 +98,8 @@ Strong exception safety: if an exception occurs, the original value stays intact 3. The function can throw the following exceptions: - Throws [`type_error.307`](../../home/exceptions.md#jsonexceptiontype_error307) when called on a type other than JSON object; example: `"cannot use erase() with null"` -4. The function can throw the following exceptions: +4. See 3. +5. The function can throw the following exceptions: - Throws [`type_error.307`](../../home/exceptions.md#jsonexceptiontype_error307) when called on a type other than JSON object; example: `"cannot use erase() with null"` - Throws [`out_of_range.401`](../../home/exceptions.md#jsonexceptionout_of_range401) when `idx >= size()`; example: @@ -103,14 +119,16 @@ Strong exception safety: if an exception occurs, the original value stays intact - strings and binary: linear in the length of the member - other types: constant 3. `log(size()) + count(key)` -4. Linear in distance between `idx` and the end of the container. +4. `log(size()) + count(key)` +5. Linear in distance between `idx` and the end of the container. ## Notes 1. Invalidates iterators and references at or after the point of the `erase`, including the `end()` iterator. -2. / +2. (none) 3. References and iterators to the erased elements are invalidated. Other references and iterators are not affected. -4. / +4. See 3. +5. (none) ## Examples @@ -156,7 +174,7 @@ Strong exception safety: if an exception occurs, the original value stays intact --8<-- "examples/erase__key_type.output" ``` -??? example "Example: (4) remove element from a JSON array given an index" +??? example "Example: (5) remove element from a JSON array given an index" The example shows the effect of `erase()` using an array index. @@ -172,5 +190,8 @@ Strong exception safety: if an exception occurs, the original value stays intact ## Version history -- 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. +3. Added in version 1.0.0. +4. Added in version 3.11.0. +5. Added in version 1.0.0. diff --git a/doc/mkdocs/docs/api/basic_json/find.md b/doc/mkdocs/docs/api/basic_json/find.md index af4cb29720..d4fddc5796 100644 --- a/doc/mkdocs/docs/api/basic_json/find.md +++ b/doc/mkdocs/docs/api/basic_json/find.md @@ -1,20 +1,28 @@ # nlohmann::basic_json::find ```cpp -template -iterator find(KeyT&& key); - -template -const_iterator find(KeyT&& key) const; +// (1) +iterator find(const typename object_t::key_type& key); +const_iterator find(const typename object_t::key_type& key) const; + +// (2) +template +iterator find(KeyType&& key); +template +const_iterator find(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 -object, `end()` is returned. +1. Finds an element in a JSON object with a key equivalent to `key`. If the element is not found or the + JSON value is not an object, `end()` is returned. +2. See 1. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and + `#!cpp typename object_comparator_t::is_transparent` denotes a type. ## 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 view (C++17). ## Parameters @@ -23,8 +31,8 @@ object, `end()` is returned. ## Return value -Iterator to an element with key equivalent to `key`. If no such element is found or the JSON value is not an object, -past-the-end (see `end()`) iterator is returned. +Iterator to an element with a key equivalent to `key`. If no such element is found or the JSON value is not an object, +a past-the-end iterator (see `end()`) is returned. ## Exception safety @@ -60,4 +68,5 @@ This method always returns `end()` when executed on a JSON type that is not an o ## Version history -- Added in version 1.0.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. diff --git a/doc/mkdocs/docs/api/basic_json/insert.md b/doc/mkdocs/docs/api/basic_json/insert.md index fdd8fe6b5e..2e6b29301e 100644 --- a/doc/mkdocs/docs/api/basic_json/insert.md +++ b/doc/mkdocs/docs/api/basic_json/insert.md @@ -50,7 +50,7 @@ void insert(const_iterator first, const_iterator last); 2. iterator pointing to the first element inserted, or `pos` if `#!cpp cnt==0` 3. iterator pointing to the first element inserted, or `pos` if `#!cpp first==last` 4. iterator pointing to the first element inserted, or `pos` if `ilist` is empty -5. / +5. (none) ## Exception safety diff --git a/doc/mkdocs/docs/api/basic_json/operator[].md b/doc/mkdocs/docs/api/basic_json/operator[].md index cc9eae7f36..cd5638b97e 100644 --- a/doc/mkdocs/docs/api/basic_json/operator[].md +++ b/doc/mkdocs/docs/api/basic_json/operator[].md @@ -6,26 +6,32 @@ reference operator[](size_type idx); const_reference operator[](size_type idx) const; // (2) -reference operator[](const typename object_t::key_type& key); +reference operator[](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; // (3) +template +reference operator[](KeyType&& key); +template +const_reference operator[](KeyType&& key) const; + +// (4) reference operator[](const json_pointer& ptr); 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`. The non-const qualified overload takes the key by value. +3. See 2. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and + `#!cpp typename object_comparator_t::is_transparent` denotes a type. +4. 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 view (C++17). ## Parameters @@ -40,9 +46,10 @@ const_reference operator[](const json_pointer& ptr) const; ## Return value -1. reference to the element at index `idx` -2. reference to the element at key `key` -3. reference to the element pointed to by `ptr` +1. (const) reference to the element at index `idx` +2. (const) reference to the element at key `key` +3. (const) reference to the element at key `key` +4. (const) reference to the element pointed to by `ptr` ## Exception safety @@ -56,7 +63,8 @@ Strong exception safety: if an exception occurs, the original value stays intact 2. The function can throw the following exceptions: - Throws [`type_error.305`](../../home/exceptions.md#jsonexceptiontype_error305) if the JSON value is not an object or null; in that case, using the `[]` operator with a key makes no sense. -3. The function can throw the following exceptions: +3. See 2. +4. The function can throw the following exceptions: - Throws [`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106) if an array index in the passed JSON pointer `ptr` begins with '0'. - Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if an array index in the passed @@ -70,7 +78,8 @@ Strong exception safety: if an exception occurs, the original value stays intact 1. Constant if `idx` is in the range of the array. Otherwise, linear in `idx - size()`. 2. Logarithmic in the size of the container. -3. Constant +3. Logarithmic in the size of the container. +4. Logarithmic in the size of the container. ## Notes @@ -87,7 +96,9 @@ Strong exception safety: if an exception occurs, the original value stays intact 2. If `key` is not found in the object, then it is silently added to the object and filled with a `#!json null` value to make `key` a valid reference. In case the value was `#!json null` before, it is converted to an object. -3. `null` values are created in arrays and objects if necessary. +3. See 2. + +4. `null` values are created in arrays and objects if necessary. In particular: @@ -143,7 +154,7 @@ Strong exception safety: if an exception occurs, the original value stays intact --8<-- "examples/operatorarray__key_type.output" ``` -??? example "Example (2): access specified object element" +??? example "Example (2): access specified object element (const)" The example below shows how object elements can be read using the `[]` operator. @@ -157,7 +168,7 @@ Strong exception safety: if an exception occurs, the original value stays intact --8<-- "examples/operatorarray__key_type_const.output" ``` -??? example "Example (3): access specified element via JSON Pointer" +??? example "Example (4): access specified element via JSON Pointer" The example below shows how values can be read and written using JSON Pointers. @@ -171,7 +182,7 @@ Strong exception safety: if an exception occurs, the original value stays intact --8<-- "examples/operatorjson_pointer.output" ``` -??? example "Example (3): access specified element via JSON Pointer" +??? example "Example (4): access specified element via JSON Pointer (const)" The example below shows how values can be read using JSON Pointers. @@ -193,5 +204,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. -3. Added in version 2.0.0. +2. Added in version 1.0.0. Added overloads for `T* key` in version 1.1.0. Removed overloads for `T* key` (replaced by 3) in version 3.11.0. +3. Added in version 3.11.0. +4. Added in version 2.0.0. diff --git a/doc/mkdocs/docs/api/basic_json/to_bson.md b/doc/mkdocs/docs/api/basic_json/to_bson.md index 664dd0e20e..5c4324a3fd 100644 --- a/doc/mkdocs/docs/api/basic_json/to_bson.md +++ b/doc/mkdocs/docs/api/basic_json/to_bson.md @@ -28,7 +28,7 @@ The exact mapping and its limitations is described on a [dedicated page](../../f ## Return value 1. BSON serialization as byte vector -2. / +2. (none) ## Exception safety diff --git a/doc/mkdocs/docs/api/basic_json/to_cbor.md b/doc/mkdocs/docs/api/basic_json/to_cbor.md index 05d85ed858..0f944c4810 100644 --- a/doc/mkdocs/docs/api/basic_json/to_cbor.md +++ b/doc/mkdocs/docs/api/basic_json/to_cbor.md @@ -29,7 +29,7 @@ The exact mapping and its limitations is described on a [dedicated page](../../f ## Return value 1. CBOR serialization as byte vector -2. / +2. (none) ## Exception safety diff --git a/doc/mkdocs/docs/api/basic_json/to_msgpack.md b/doc/mkdocs/docs/api/basic_json/to_msgpack.md index fb4b40bd03..7d40981d5f 100644 --- a/doc/mkdocs/docs/api/basic_json/to_msgpack.md +++ b/doc/mkdocs/docs/api/basic_json/to_msgpack.md @@ -28,7 +28,7 @@ The exact mapping and its limitations is described on a [dedicated page](../../f ## Return value 1. MessagePack serialization as byte vector -2. / +2. (none) ## Exception safety diff --git a/doc/mkdocs/docs/api/basic_json/to_ubjson.md b/doc/mkdocs/docs/api/basic_json/to_ubjson.md index 0a3d87e544..e3cd5d62b9 100644 --- a/doc/mkdocs/docs/api/basic_json/to_ubjson.md +++ b/doc/mkdocs/docs/api/basic_json/to_ubjson.md @@ -39,7 +39,7 @@ The exact mapping and its limitations is described on a [dedicated page](../../f ## Return value 1. UBJSON serialization as byte vector -2. / +2. (none) ## Exception safety diff --git a/doc/mkdocs/docs/api/basic_json/value.md b/doc/mkdocs/docs/api/basic_json/value.md index 1844c41fb0..6a1f3481d5 100644 --- a/doc/mkdocs/docs/api/basic_json/value.md +++ b/doc/mkdocs/docs/api/basic_json/value.md @@ -4,9 +4,14 @@ // (1) template ValueType value(const typename object_t::key_type& key, - const ValueType& default_value) const; + ValueType&& default_value) const; // (2) +template +ValueType value(KeyType&& key, + ValueType&& default_value) const; + +// (3) template ValueType value(const json_pointer& ptr, const ValueType& default_value) const; @@ -24,7 +29,10 @@ ValueType value(const json_pointer& ptr, } ``` -2. Returns either a copy of an object's element at the specified JSON pointer `ptr` or a given default value if no value +2. See 1. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and + `#!cpp typename object_comparator_t::is_transparent` denotes a type. + +3. Returns either a copy of an object's element at the specified JSON pointer `ptr` or a given default value if no value at `ptr` exists. The function is basically equivalent to executing @@ -44,6 +52,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 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 @@ -55,7 +67,7 @@ ValueType value(const json_pointer& ptr, : key of the element to access `default_value` (in) -: the value to return if key/ptr found no value +: the value to return if `key`/`ptr` found no value `ptr` (in) : a JSON pointer to the element to access @@ -63,7 +75,8 @@ ValueType value(const json_pointer& ptr, ## Return value 1. copy of the element at key `key` or `default_value` if `key` is not found -1. copy of the element at JSON Pointer `ptr` or `default_value` if no value for `ptr` is found +2. copy of the element at key `key` or `default_value` if `key` is not found +3. copy of the element at JSON Pointer `ptr` or `default_value` if no value for `ptr` is found ## Exception safety @@ -77,7 +90,8 @@ changes to any JSON value. the type of the value at `key` - Throws [`type_error.306`](../../home/exceptions.md#jsonexceptiontype_error306) if the JSON value is not an object; in that case, using `value()` with a key makes no sense. -2. The function can throw the following exceptions: +2. See 1. +3. The function can throw the following exceptions: - Throws [`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) if `default_value` does not match the type of the value at `ptr` - Throws [`type_error.306`](../../home/exceptions.md#jsonexceptiontype_error306) if the JSON value is not an object; @@ -87,6 +101,7 @@ changes to any JSON value. 1. Logarithmic in the size of the container. 2. Logarithmic in the size of the container. +3. Logarithmic in the size of the container. ## Examples @@ -104,7 +119,7 @@ changes to any JSON value. --8<-- "examples/basic_json__value.output" ``` -??? example "Example (2): access specified object element via JSON Pointer with default value" +??? example "Example (3): access specified object element via JSON Pointer with default value" The example below shows how object elements can be queried with a default value. @@ -125,5 +140,6 @@ changes to any JSON value. ## Version history -1. Added in version 1.0.0. -2. Added in version 2.0.2. +1. Added in version 1.0.0. Changed parameter `default_value` type from `const ValueType&` to `ValueType&&` in version 3.11.0. +2. Added in version 3.11.0. +3. Added in version 2.0.2. diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 51eb737782..24453dd521 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -2006,22 +2006,37 @@ 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::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()) + { + JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward(key)), "' not found"), this)); + } + return set_parent(it->second); } /// @brief access specified object element with bounds checking @@ -2029,22 +2044,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 '", 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::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(std::forward(key)), "' not found"), this)); + } + return it->second; } /// @brief access specified array element @@ -2108,7 +2138,7 @@ 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) + reference operator[](typename object_t::key_type key) { // implicitly convert null value to an empty object if (is_null()) @@ -2121,7 +2151,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::move(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)); @@ -2134,31 +2165,47 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // 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(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)); } - /// @brief access specified object element - /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + // these two functions resolve a (const) char * ambiguity affecting Clang and MSVC + // (they seemingly cannot be constrained to resolve the ambiguity) template - JSON_HEDLEY_NON_NULL(2) reference operator[](T* key) { - // implicitly convert null to object + return operator[](typename object_t::key_type(key)); + } + + template + const_reference operator[](T* key) const + { + return operator[](typename object_t::key_type(key)); + } + + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + template::value, int > = 0 > + reference operator[](KeyType && key) + { + // implicitly convert null value to an empty object if (is_null()) { m_type = value_t::object; - m_value = value_t::object; + m_value.object = create(); 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)); + 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)); @@ -2166,15 +2213,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/ - template - JSON_HEDLEY_NON_NULL(2) - const_reference operator[](T* key) const + template::value, int > = 0 > + const_reference operator[](KeyType && key) const { - // at only works for objects + // 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)); @@ -2182,23 +2230,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 < - 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 + template < class KeyType, class ValueType, detail::enable_if_t < + std::is_same::value + && detail::is_getable::value + && !std::is_same::value, int > = 0 > + typename std::decay::type value(const 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); 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)); @@ -2212,13 +2260,64 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return value(key, string_t(default_value)); } + // these two functions, in conjunction with value(const KeyType &, ValueType &&), + // resolve an ambiguity that would otherwise occur between the json_pointer and + // typename object_t::key_type & overloads + template < class ValueType, detail::enable_if_t < + detail::is_getable::value + && !std::is_same::value, int > = 0 > + typename std::decay::type value(const char* key, ValueType && default_value) const + { + return value(typename object_t::key_type(key), std::forward(default_value)); + } + + string_t value(const char* key, const char* default_value) const + { + return value(typename object_t::key_type(key), string_t(default_value)); + } + + /// @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 KeyType, class ValueType, detail::enable_if_t < + detail::is_getable::value + && !std::is_same::value + && detail::is_usable_as_key_type::value, int > = 0 > + typename std::decay::type value(KeyType && key, ValueType && default_value) const + { + // 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(std::forward(key)); + if (it != end()) + { + return it->template get::type>(); + } + + return std::forward(default_value); + } + + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); + } + + /// @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* + 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(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 +2334,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 +2393,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 +2463,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 +2534,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())) { - 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)); + 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()) + { + 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 added 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::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 +2619,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::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; @@ -2497,14 +2661,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::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 +2677,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::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::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 @@ -4230,7 +4411,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return ptr.get_unchecked(this); } - template + template::value, int> = 0> JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) reference operator[](const ::nlohmann::json_pointer& ptr) { @@ -4244,7 +4425,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return ptr.get_unchecked(this); } - template + template::value, int> = 0> JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) const_reference operator[](const ::nlohmann::json_pointer& ptr) const { diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 5a5c604737..2edd0b0659 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -19553,22 +19553,37 @@ 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::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()) + { + JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward(key)), "' not found"), this)); + } + return set_parent(it->second); } /// @brief access specified object element with bounds checking @@ -19576,22 +19591,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 '", 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::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(std::forward(key)), "' not found"), this)); + } + return it->second; } /// @brief access specified array element @@ -19655,7 +19685,7 @@ 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) + reference operator[](typename object_t::key_type key) { // implicitly convert null value to an empty object if (is_null()) @@ -19668,7 +19698,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::move(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)); @@ -19681,31 +19712,47 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // 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(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)); } - /// @brief access specified object element - /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + // these two functions resolve a (const) char * ambiguity affecting Clang and MSVC + // (they seemingly cannot be constrained to resolve the ambiguity) template - JSON_HEDLEY_NON_NULL(2) reference operator[](T* key) { - // implicitly convert null to object + return operator[](typename object_t::key_type(key)); + } + + template + const_reference operator[](T* key) const + { + return operator[](typename object_t::key_type(key)); + } + + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + template::value, int > = 0 > + reference operator[](KeyType && key) + { + // implicitly convert null value to an empty object if (is_null()) { m_type = value_t::object; - m_value = value_t::object; + m_value.object = create(); 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)); + 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)); @@ -19713,15 +19760,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/ - template - JSON_HEDLEY_NON_NULL(2) - const_reference operator[](T* key) const + template::value, int > = 0 > + const_reference operator[](KeyType && key) const { - // at only works for objects + // 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)); @@ -19729,23 +19777,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 < - 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 + template < class KeyType, class ValueType, detail::enable_if_t < + std::is_same::value + && detail::is_getable::value + && !std::is_same::value, int > = 0 > + typename std::decay::type value(const 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); 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)); @@ -19759,13 +19807,64 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return value(key, string_t(default_value)); } + // these two functions, in conjunction with value(const KeyType &, ValueType &&), + // resolve an ambiguity that would otherwise occur between the json_pointer and + // typename object_t::key_type & overloads + template < class ValueType, detail::enable_if_t < + detail::is_getable::value + && !std::is_same::value, int > = 0 > + typename std::decay::type value(const char* key, ValueType && default_value) const + { + return value(typename object_t::key_type(key), std::forward(default_value)); + } + + string_t value(const char* key, const char* default_value) const + { + return value(typename object_t::key_type(key), string_t(default_value)); + } + + /// @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 KeyType, class ValueType, detail::enable_if_t < + detail::is_getable::value + && !std::is_same::value + && detail::is_usable_as_key_type::value, int > = 0 > + typename std::decay::type value(KeyType && key, ValueType && default_value) const + { + // 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(std::forward(key)); + if (it != end()) + { + return it->template get::type>(); + } + + return std::forward(default_value); + } + + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); + } + + /// @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* + 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(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 @@ -19782,8 +19881,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 { @@ -19841,10 +19940,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 @@ -19912,10 +20010,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 @@ -19984,17 +20081,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())) { - 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)); + 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()) + { + 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 added 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::value, int> = 0> + size_type erase(KeyType && key) + { + return erase_internal(std::forward(key)); } /// @brief remove element from a JSON array given an index @@ -20029,14 +20166,27 @@ 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::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; @@ -20044,14 +20194,29 @@ 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 + 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(std::forward(key)); + result.m_it.object_iterator = m_value.object->find(key); + } + + return result; + } + + /// @brief find an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/find/ + template::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)); } return result; @@ -20059,20 +20224,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::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::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 @@ -21777,7 +21958,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return ptr.get_unchecked(this); } - template + template::value, int> = 0> JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) reference operator[](const ::nlohmann::json_pointer& ptr) { @@ -21791,7 +21972,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return ptr.get_unchecked(this); } - template + template::value, int> = 0> JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) const_reference operator[](const ::nlohmann::json_pointer& ptr) const { 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 } } }