mirror of
https://github.com/nlohmann/json.git
synced 2026-10-01 12:10:32 +00:00
Compare commits
17
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1595c1b40 | ||
|
|
3d6d610fdc | ||
|
|
05b6cd0892 | ||
|
|
95d10dab70 | ||
|
|
da971a52c8 | ||
|
|
4c6a64507b | ||
|
|
c62aa8ac67 | ||
|
|
a7256deba3 | ||
|
|
3a4eddf4ac | ||
|
|
43e2d9c525 | ||
|
|
0650a48659 | ||
|
|
29bb5c48b8 | ||
|
|
e17e23a2b1 | ||
|
|
c2d7177d30 | ||
|
|
08dcdaf9b8 | ||
|
|
7dae258350 | ||
|
|
7da943c5c4 |
@@ -70,13 +70,17 @@ cc_library(
|
|||||||
"include/nlohmann/detail/view/document_data.hpp",
|
"include/nlohmann/detail/view/document_data.hpp",
|
||||||
"include/nlohmann/detail/view/errors.hpp",
|
"include/nlohmann/detail/view/errors.hpp",
|
||||||
"include/nlohmann/detail/view/input.hpp",
|
"include/nlohmann/detail/view/input.hpp",
|
||||||
|
"include/nlohmann/detail/view/iterator.hpp",
|
||||||
|
"include/nlohmann/detail/view/lookup.hpp",
|
||||||
"include/nlohmann/detail/view/macro_scope.hpp",
|
"include/nlohmann/detail/view/macro_scope.hpp",
|
||||||
"include/nlohmann/detail/view/macro_unscope.hpp",
|
"include/nlohmann/detail/view/macro_unscope.hpp",
|
||||||
"include/nlohmann/detail/view/materialize.hpp",
|
"include/nlohmann/detail/view/materialize.hpp",
|
||||||
"include/nlohmann/detail/view/node.hpp",
|
"include/nlohmann/detail/view/node.hpp",
|
||||||
"include/nlohmann/detail/view/number.hpp",
|
"include/nlohmann/detail/view/number.hpp",
|
||||||
|
"include/nlohmann/detail/view/pointer.hpp",
|
||||||
"include/nlohmann/detail/view/scan.hpp",
|
"include/nlohmann/detail/view/scan.hpp",
|
||||||
"include/nlohmann/detail/view/string_ref.hpp",
|
"include/nlohmann/detail/view/string_ref.hpp",
|
||||||
|
"include/nlohmann/detail/view/value.hpp",
|
||||||
"include/nlohmann/json.hpp",
|
"include/nlohmann/json.hpp",
|
||||||
"include/nlohmann/json_fwd.hpp",
|
"include/nlohmann/json_fwd.hpp",
|
||||||
"include/nlohmann/json_literals.hpp",
|
"include/nlohmann/json_literals.hpp",
|
||||||
|
|||||||
@@ -143,7 +143,20 @@ INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::shrink_t
|
|||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::source', 'Method', 'api/basic_json_document/source/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::source', 'Method', 'api/basic_json_document/source/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view', 'Class', 'api/basic_json_view/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view', 'Class', 'api/basic_json_view/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::basic_json_view', 'Constructor', 'api/basic_json_view/basic_json_view/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::basic_json_view', 'Constructor', 'api/basic_json_view/basic_json_view/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::at', 'Method', 'api/basic_json_view/at/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::back', 'Method', 'api/basic_json_view/back/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::begin', 'Method', 'api/basic_json_view/begin/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::cbegin', 'Method', 'api/basic_json_view/cbegin/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::cend', 'Method', 'api/basic_json_view/cend/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::contains', 'Method', 'api/basic_json_view/contains/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::count', 'Method', 'api/basic_json_view/count/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::empty', 'Method', 'api/basic_json_view/empty/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::empty', 'Method', 'api/basic_json_view/empty/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::end', 'Method', 'api/basic_json_view/end/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::find', 'Method', 'api/basic_json_view/find/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::front', 'Method', 'api/basic_json_view/front/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::get', 'Method', 'api/basic_json_view/get/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::get_string', 'Method', 'api/basic_json_view/get_string/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::get_to', 'Method', 'api/basic_json_view/get_to/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::is_array', 'Method', 'api/basic_json_view/is_array/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::is_array', 'Method', 'api/basic_json_view/is_array/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::is_binary', 'Method', 'api/basic_json_view/is_binary/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::is_binary', 'Method', 'api/basic_json_view/is_binary/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::is_boolean', 'Method', 'api/basic_json_view/is_boolean/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::is_boolean', 'Method', 'api/basic_json_view/is_boolean/index.html');
|
||||||
@@ -157,11 +170,16 @@ INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::is_object',
|
|||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::is_primitive', 'Method', 'api/basic_json_view/is_primitive/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::is_primitive', 'Method', 'api/basic_json_view/is_primitive/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::is_string', 'Method', 'api/basic_json_view/is_string/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::is_string', 'Method', 'api/basic_json_view/is_string/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::is_structured', 'Method', 'api/basic_json_view/is_structured/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::is_structured', 'Method', 'api/basic_json_view/is_structured/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::items', 'Method', 'api/basic_json_view/items/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::materialize', 'Method', 'api/basic_json_view/materialize/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::materialize', 'Method', 'api/basic_json_view/materialize/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::number_token', 'Method', 'api/basic_json_view/number_token/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::operator bool', 'Method', 'api/basic_json_view/operator_bool/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::operator bool', 'Method', 'api/basic_json_view/operator_bool/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::operator[]', 'Operator', 'api/basic_json_view/operator[]/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::size', 'Method', 'api/basic_json_view/size/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::size', 'Method', 'api/basic_json_view/size/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::source_offset', 'Method', 'api/basic_json_view/source_offset/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::source_offset', 'Method', 'api/basic_json_view/source_offset/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::type', 'Method', 'api/basic_json_view/type/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::type', 'Method', 'api/basic_json_view/type/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::type_name', 'Method', 'api/basic_json_view/type_name/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::value', 'Method', 'api/basic_json_view/value/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json', 'Class', 'api/json/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('json', 'Class', 'api/json/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_document', 'Class', 'api/json_document/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_document', 'Class', 'api/json_document/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_view', 'Class', 'api/json_view/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_view', 'Class', 'api/json_view/index.html');
|
||||||
|
|||||||
@@ -219,6 +219,7 @@ Strong exception safety: if an exception occurs, the original value stays intact
|
|||||||
- documentation on [checked access](../../features/element_access/checked_access.md)
|
- documentation on [checked access](../../features/element_access/checked_access.md)
|
||||||
- [`operator[]`](operator%5B%5D.md) for unchecked access by reference
|
- [`operator[]`](operator%5B%5D.md) for unchecked access by reference
|
||||||
- [`value`](value.md) for access with default value
|
- [`value`](value.md) for access with default value
|
||||||
|
- [basic_json_view::at](../basic_json_view/at.md) - the same access on a zero-copy view
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ Constant.
|
|||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [front](front.md) to access the first element
|
- [front](front.md) to access the first element
|
||||||
|
- [basic_json_view::back](../basic_json_view/back.md) - the same access on a zero-copy view
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,12 @@ Constant.
|
|||||||
--8<-- "examples/begin.output"
|
--8<-- "examples/begin.output"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [end](end.md) - returns an iterator to one past the last element
|
||||||
|
- [basic_json_view::begin](../basic_json_view/begin.md) - the same iteration on a zero-copy view (in document order,
|
||||||
|
not sorted by key)
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 1.0.0.
|
- Added in version 1.0.0.
|
||||||
|
|||||||
@@ -36,6 +36,11 @@ Constant.
|
|||||||
--8<-- "examples/cbegin.output"
|
--8<-- "examples/cbegin.output"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [cend](cend.md) - returns a const iterator to one past the last element
|
||||||
|
- [basic_json_view::cbegin](../basic_json_view/cbegin.md) - the same iteration on a zero-copy view
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 1.0.0.
|
- Added in version 1.0.0.
|
||||||
|
|||||||
@@ -36,6 +36,11 @@ Constant.
|
|||||||
--8<-- "examples/cend.output"
|
--8<-- "examples/cend.output"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [cbegin](cbegin.md) - returns a const iterator to the first element
|
||||||
|
- [basic_json_view::cend](../basic_json_view/cend.md) - the same iteration on a zero-copy view
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 1.0.0.
|
- Added in version 1.0.0.
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ Logarithmic in the size of the JSON object.
|
|||||||
|
|
||||||
- [find](find.md) find a value in an object
|
- [find](find.md) find a value in an object
|
||||||
- [count](count.md) returns the number of occurrences of a key
|
- [count](count.md) returns the number of occurrences of a key
|
||||||
|
- [basic_json_view::contains](../basic_json_view/contains.md) - the same check on a zero-copy view
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ Logarithmic in the size of the JSON object.
|
|||||||
|
|
||||||
- [find](find.md) find a value in an object
|
- [find](find.md) find a value in an object
|
||||||
- [contains](contains.md) checks whether a key exists
|
- [contains](contains.md) checks whether a key exists
|
||||||
|
- [basic_json_view::count](../basic_json_view/count.md) - the same check on a zero-copy view
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,11 @@ Constant.
|
|||||||
--8<-- "examples/end.output"
|
--8<-- "examples/end.output"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [begin](begin.md) - returns an iterator to the first element
|
||||||
|
- [basic_json_view::end](../basic_json_view/end.md) - the same iteration on a zero-copy view
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 1.0.0.
|
- Added in version 1.0.0.
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ Logarithmic in the size of the JSON object.
|
|||||||
|
|
||||||
- [count](count.md) returns the number of occurrences of a key
|
- [count](count.md) returns the number of occurrences of a key
|
||||||
- [contains](contains.md) checks whether a key exists
|
- [contains](contains.md) checks whether a key exists
|
||||||
|
- [basic_json_view::find](../basic_json_view/find.md) - the same lookup on a zero-copy view
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ Constant.
|
|||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [back](back.md) to access the last element
|
- [back](back.md) to access the last element
|
||||||
|
- [basic_json_view::front](../basic_json_view/front.md) - the same access on a zero-copy view
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -163,6 +163,8 @@ overload (3).
|
|||||||
- [get_ref](get_ref.md) get a reference to the stored value
|
- [get_ref](get_ref.md) get a reference to the stored value
|
||||||
- [operator ValueType](operator_ValueType.md) get a value via implicit conversion
|
- [operator ValueType](operator_ValueType.md) get a value via implicit conversion
|
||||||
- [Converting values](../../features/conversions.md) - the type conversions article
|
- [Converting values](../../features/conversions.md) - the type conversions article
|
||||||
|
- [basic_json_view::get](../basic_json_view/get.md) - the same conversion on a zero-copy view (many types are
|
||||||
|
converted without ever building a `basic_json` value)
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ Constant.
|
|||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [get_ptr()](get_ptr.md) get a pointer value
|
- [get_ptr()](get_ptr.md) get a pointer value
|
||||||
|
- [basic_json_view::get_string](../basic_json_view/get_string.md) - the closest counterpart on a zero-copy view: a
|
||||||
|
string without a copy, but as a view rather than a reference to a value that must already exist
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ Depends on the `json_serializer<ValueType>::from_json()` implementation.
|
|||||||
- [get_ref](get_ref.md) get a reference to the stored value
|
- [get_ref](get_ref.md) get a reference to the stored value
|
||||||
- [get_ptr](get_ptr.md) get a pointer to the stored value
|
- [get_ptr](get_ptr.md) get a pointer to the stored value
|
||||||
- [Converting values](../../features/conversions.md) - the type conversions article
|
- [Converting values](../../features/conversions.md) - the type conversions article
|
||||||
|
- [basic_json_view::get_to](../basic_json_view/get_to.md) - the same conversion on a zero-copy view
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -99,6 +99,8 @@ When iterating over an array, `key()` will return the index of the element as st
|
|||||||
|
|
||||||
- [begin](begin.md) returns an iterator to the first element
|
- [begin](begin.md) returns an iterator to the first element
|
||||||
- [end](end.md) returns an iterator to one past the last element
|
- [end](end.md) returns an iterator to one past the last element
|
||||||
|
- [basic_json_view::items](../basic_json_view/items.md) - the same range on a zero-copy view (`#!cpp const auto`,
|
||||||
|
not `#!cpp const auto&`: items are produced on the fly)
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -257,6 +257,8 @@ Strong exception safety: if an exception occurs, the original value stays intact
|
|||||||
- documentation on [runtime assertions](../../features/assertions.md)
|
- documentation on [runtime assertions](../../features/assertions.md)
|
||||||
- see [`at`](at.md) for access by reference with range checking
|
- see [`at`](at.md) for access by reference with range checking
|
||||||
- see [`value`](value.md) for access with default value
|
- see [`value`](value.md) for access with default value
|
||||||
|
- [basic_json_view::operator[]](../basic_json_view/operator%5B%5D.md) - the same access on a zero-copy view (always
|
||||||
|
returns a discarded view instead of assuming undefined behavior)
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,11 @@ Constant.
|
|||||||
--8<-- "examples/type_name.output"
|
--8<-- "examples/type_name.output"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [type](type.md) - return the type of the JSON value
|
||||||
|
- [basic_json_view::type_name](../basic_json_view/type_name.md) - the same function on a zero-copy view
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 1.0.0.
|
- Added in version 1.0.0.
|
||||||
|
|||||||
@@ -189,6 +189,7 @@ changes to any JSON value.
|
|||||||
|
|
||||||
- see [`at`](at.md) for access by reference with range checking
|
- see [`at`](at.md) for access by reference with range checking
|
||||||
- see [`operator[]`](operator%5B%5D.md) for unchecked access by reference
|
- see [`operator[]`](operator%5B%5D.md) for unchecked access by reference
|
||||||
|
- [basic_json_view::value](../basic_json_view/value.md) - the same access on a zero-copy view
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,132 @@
|
|||||||
|
# <small>nlohmann::basic_json_view::</small>at
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// (1)
|
||||||
|
basic_json_view at(string_view_t key) const;
|
||||||
|
basic_json_view at(const char* key) const;
|
||||||
|
basic_json_view at(const string_t& key) const;
|
||||||
|
|
||||||
|
// (2)
|
||||||
|
basic_json_view at(size_type idx) const;
|
||||||
|
basic_json_view at(int idx) const;
|
||||||
|
|
||||||
|
// (3)
|
||||||
|
basic_json_view at(const json_pointer& ptr) const;
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Returns the value of the object member with key `key` -- the first one, should the key occur more than once (see
|
||||||
|
[Notes on duplicate keys](operator[].md#notes)).
|
||||||
|
2. Returns the array element at index `idx`.
|
||||||
|
3. Returns the value a JSON pointer `ptr` refers to, starting at this value.
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
`key` (in)
|
||||||
|
: object key of the element to access
|
||||||
|
|
||||||
|
`idx` (in)
|
||||||
|
: index of the element to access
|
||||||
|
|
||||||
|
`ptr` (in)
|
||||||
|
: JSON pointer to the element to access
|
||||||
|
|
||||||
|
## Return value
|
||||||
|
|
||||||
|
1. the value of the first member with key `key`
|
||||||
|
2. the element at index `idx`
|
||||||
|
3. the value `ptr` resolves to, starting at this value
|
||||||
|
|
||||||
|
## Exception safety
|
||||||
|
|
||||||
|
Strong exception safety: if an exception is thrown, there are no changes to the view or the document it refers to.
|
||||||
|
|
||||||
|
## Exceptions
|
||||||
|
|
||||||
|
1. The function can throw the following exceptions, both with the same message as the corresponding call to
|
||||||
|
[`BasicJsonType::at`](../basic_json/at.md):
|
||||||
|
- Throws [`type_error.304`](../../home/exceptions.md#jsonexceptiontype_error304) if the value is not an object.
|
||||||
|
- Throws [`out_of_range.403`](../../home/exceptions.md#jsonexceptionout_of_range403) if no member has key `key`.
|
||||||
|
2. The function can throw the following exceptions, both with the same message as the corresponding call to
|
||||||
|
[`BasicJsonType::at`](../basic_json/at.md):
|
||||||
|
- Throws [`type_error.304`](../../home/exceptions.md#jsonexceptiontype_error304) if the value is not an array.
|
||||||
|
- Throws [`out_of_range.401`](../../home/exceptions.md#jsonexceptionout_of_range401) if `#!cpp idx >= size()`.
|
||||||
|
3. The function can throw the following exceptions, all with the same message as the corresponding call to
|
||||||
|
[`BasicJsonType::at`](../basic_json/at.md):
|
||||||
|
- Throws [`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106) if an array index in `ptr`
|
||||||
|
begins with `#!cpp '0'`.
|
||||||
|
- Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if an array index in `ptr` is
|
||||||
|
not a number.
|
||||||
|
- Throws [`out_of_range.401`](../../home/exceptions.md#jsonexceptionout_of_range401) if an array index in `ptr`
|
||||||
|
is out of range.
|
||||||
|
- Throws [`out_of_range.402`](../../home/exceptions.md#jsonexceptionout_of_range402) if a reference token is
|
||||||
|
`#!cpp "-"` at an array -- `at` never inserts an element, so `#!cpp "-"` is always invalid.
|
||||||
|
- Throws [`out_of_range.403`](../../home/exceptions.md#jsonexceptionout_of_range403) if a reference token names
|
||||||
|
an object member that does not exist.
|
||||||
|
- Throws [`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) if `ptr` cannot be resolved
|
||||||
|
because a reference token is used on a primitive value.
|
||||||
|
|
||||||
|
None of these exceptions carry a [`JSON_DIAGNOSTICS`](../macros/json_diagnostics.md) path: the view has no
|
||||||
|
`BasicJsonType` value to point at, so the exception is created without one, even if `BasicJsonType` was built with
|
||||||
|
`JSON_DIAGNOSTICS` enabled.
|
||||||
|
|
||||||
|
## Complexity
|
||||||
|
|
||||||
|
1. Linear in the number of members: as for [`ordered_json`](../ordered_json.md), members are compared one after
|
||||||
|
another, in document order, stopping at the first match. Each comparison first checks the key's length --
|
||||||
|
already known from the index, without reading the key bytes -- before comparing its content.
|
||||||
|
2. Linear in `idx`: elements are skipped one at a time from the first one, since they are not a fixed size in the
|
||||||
|
index (unlike `BasicJsonType`'s array, which is random-access).
|
||||||
|
3. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
||||||
|
that level (as 1.) or the index into the array (as 2.).
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
Unlike [`operator[]`](operator[].md), which returns a [discarded](is_discarded.md) view for a missing key or an
|
||||||
|
out-of-range index, `at` always throws -- exactly as `BasicJsonType::at` does, and with the same messages, so
|
||||||
|
existing error handling written against `BasicJsonType::at` keeps working unchanged when switched to a view. This
|
||||||
|
also holds for overload 3: unlike [`operator[]`](operator[].md) with a JSON pointer, which returns a discarded view
|
||||||
|
for a missing key or an out-of-range index, `at` throws for those too (`out_of_range.403`/`out_of_range.401`).
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example "Example: (1)/(2) access specified element with bounds checking"
|
||||||
|
|
||||||
|
The example below reads required fields out of a service configuration with `at`, and shows that the exceptions
|
||||||
|
it throws -- for a wrong type and for a missing key -- carry the same messages
|
||||||
|
[`BasicJsonType::at`](../basic_json/at.md) would produce for the same JSON text.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__at.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__at.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
??? example "Example: (3) access specified element via JSON pointer with bounds checking"
|
||||||
|
|
||||||
|
The example below shows that `at` with a JSON pointer throws exactly the exceptions, with exactly the messages,
|
||||||
|
that [`BasicJsonType::at`](../basic_json/at.md) throws for the same pointer and the same document.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__at_json_pointer.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__at_json_pointer.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [operator[]](operator[].md) - access specified element (returns a discarded view instead of throwing)
|
||||||
|
- [front](front.md), [back](back.md) - access the first or last element
|
||||||
|
- [`BasicJsonType::at`](../basic_json/at.md) - the corresponding function of `basic_json`
|
||||||
|
- [`json_pointer`](../json_pointer/index.md) - JSON pointer type used by overload 3
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# <small>nlohmann::basic_json_view::</small>back
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
basic_json_view back() const;
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns the last element of an array, the last member value of an object, or the value itself if it is primitive
|
||||||
|
(as for [`BasicJsonType::back()`](../basic_json/back.md), a primitive value is a range of one element).
|
||||||
|
|
||||||
|
## Return value
|
||||||
|
|
||||||
|
The last element or member value. For a primitive value (number, string, boolean), the value itself.
|
||||||
|
|
||||||
|
## Exception safety
|
||||||
|
|
||||||
|
Strong exception safety: if an exception is thrown, there are no changes to the view or the document it refers to.
|
||||||
|
|
||||||
|
## Exceptions
|
||||||
|
|
||||||
|
Throws [`invalid_iterator.214`](../../home/exceptions.md#jsonexceptioninvalid_iterator214) if the view is
|
||||||
|
[null](is_null.md) or [discarded](is_discarded.md), or if it is an empty array or object.
|
||||||
|
|
||||||
|
This exception does not carry a [`JSON_DIAGNOSTICS`](../macros/json_diagnostics.md) path: the view has no
|
||||||
|
`BasicJsonType` value to point at, so the exception is created without one, even if `BasicJsonType` was built with
|
||||||
|
`JSON_DIAGNOSTICS` enabled.
|
||||||
|
|
||||||
|
## Complexity
|
||||||
|
|
||||||
|
Linear in the size of the array or object: unlike [`front()`](front.md), which only ever looks at the first
|
||||||
|
element, `back()` has to walk every element to find where they end, since elements are not a fixed size in the
|
||||||
|
index.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
Unlike [`BasicJsonType::back()`](../basic_json/back.md), which has undefined behavior for an empty array or object,
|
||||||
|
`back()` throws `invalid_iterator.214` in that case -- the same way it already does for `#!json null` and for a
|
||||||
|
discarded view, where `BasicJsonType::back()` also throws.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example
|
||||||
|
|
||||||
|
The example below reads only the final status of a build log with `back()`. Even though `back()` is linear in
|
||||||
|
the number of events (unlike [`front()`](front.md), which is constant), it still avoids building a
|
||||||
|
`BasicJsonType` value for the events that are not needed.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__back.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__back.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [front](front.md) - access the first element
|
||||||
|
- [`BasicJsonType::back`](../basic_json/back.md) - the corresponding function of `basic_json`
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -8,8 +8,9 @@ Creates an invalid (discarded) view: [`type()`](type.md) is `#!cpp value_t::disc
|
|||||||
[`is_discarded()`](is_discarded.md) is `#!cpp true`, and `#!cpp explicit operator bool()` is `#!cpp false`.
|
[`is_discarded()`](is_discarded.md) is `#!cpp true`, and `#!cpp explicit operator bool()` is `#!cpp false`.
|
||||||
|
|
||||||
This is the only constructor a caller can use directly. Every other view is obtained from a
|
This is the only constructor a caller can use directly. Every other view is obtained from a
|
||||||
[`basic_json_document`](../basic_json_document/index.md), via [`root()`](../basic_json_document/root.md) or (once
|
[`basic_json_document`](../basic_json_document/index.md), via [`root()`](../basic_json_document/root.md) or by
|
||||||
element access is added) from navigating into a container.
|
navigating into a container with [`operator[]`](operator[].md), [`at`](at.md), [`front`](front.md), [`back`](back.md),
|
||||||
|
[`find`](find.md), or iteration.
|
||||||
|
|
||||||
## Exception safety
|
## Exception safety
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# <small>nlohmann::basic_json_view::</small>begin
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
iterator begin() const noexcept;
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns an iterator to the first element of an array, or the first member value of an object, in **document order**
|
||||||
|
-- the order the values appear in the source text, not sorted by key. A primitive value iterates as a range of one
|
||||||
|
element (itself); `#!json null` and a [discarded](is_discarded.md) view iterate as an empty range.
|
||||||
|
|
||||||
|
## Return value
|
||||||
|
|
||||||
|
Iterator to the first element.
|
||||||
|
|
||||||
|
## Exception safety
|
||||||
|
|
||||||
|
No-throw guarantee: this function never throws exceptions.
|
||||||
|
|
||||||
|
## Complexity
|
||||||
|
|
||||||
|
Constant.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
For an object, iteration visits **every** member, including all occurrences of a duplicate key -- unlike
|
||||||
|
[`operator[]`](operator[].md), [`at`](at.md), [`find`](find.md), [`contains`](contains.md), and [`count`](count.md),
|
||||||
|
which all resolve to the *first* member with a given key. See the
|
||||||
|
[Notes on duplicate keys](operator[].md#notes) of `operator[]`.
|
||||||
|
|
||||||
|
Because objects are iterated in document order rather than sorted by key, the order seen here can differ from what
|
||||||
|
iterating the [`materialize()`](materialize.md)d `BasicJsonType` value would produce: a `#!cpp basic_json` object
|
||||||
|
(`std::map`-backed by default) sorts its keys, while a view does not.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example
|
||||||
|
|
||||||
|
The example below iterates a log record's members with `begin()`/[`end()`](end.md) and prints them in the order
|
||||||
|
they were written. Materializing the record into a `BasicJsonType` object and iterating that would instead print
|
||||||
|
the members sorted by key.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__begin.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__begin.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [end](end.md) - returns an iterator to one past the last element
|
||||||
|
- [cbegin](cbegin.md) - returns a const iterator to the first element
|
||||||
|
- [items](items.md) - access iterator member functions in range-based for
|
||||||
|
- [`BasicJsonType::begin`](../basic_json/begin.md) - the corresponding function of `basic_json`
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# <small>nlohmann::basic_json_view::</small>cbegin
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
iterator cbegin() const noexcept;
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns an iterator to the first element, in [document order](begin.md). Equivalent to [`begin()`](begin.md): a view
|
||||||
|
is always read-only, so `#!cpp const_iterator` and `#!cpp iterator` are the same type, and `cbegin()` exists only so
|
||||||
|
that generic code that expects a `cbegin()`/`cend()` pair works with a view too.
|
||||||
|
|
||||||
|
## Return value
|
||||||
|
|
||||||
|
Iterator to the first element; identical to what [`begin()`](begin.md) returns.
|
||||||
|
|
||||||
|
## Exception safety
|
||||||
|
|
||||||
|
No-throw guarantee: this function never throws exceptions.
|
||||||
|
|
||||||
|
## Complexity
|
||||||
|
|
||||||
|
Constant.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example
|
||||||
|
|
||||||
|
The example below sums many measurements with `std::accumulate`, using `cbegin()`/[`cend()`](cend.md) as the
|
||||||
|
range -- the same way it would for any standard container -- without ever materializing the whole array into a
|
||||||
|
`BasicJsonType` value.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__cbegin.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__cbegin.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [begin](begin.md) - returns an iterator to the first element
|
||||||
|
- [cend](cend.md) - returns a const iterator to one past the last element
|
||||||
|
- [`BasicJsonType::cbegin`](../basic_json/cbegin.md) - the corresponding function of `basic_json`
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# <small>nlohmann::basic_json_view::</small>cend
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
iterator cend() const noexcept;
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns an iterator to one past the last element, in [document order](begin.md). Equivalent to [`end()`](end.md): a
|
||||||
|
view is always read-only, so `#!cpp const_iterator` and `#!cpp iterator` are the same type, and `cend()` exists only
|
||||||
|
so that generic code that expects a `cbegin()`/`cend()` pair works with a view too.
|
||||||
|
|
||||||
|
## Return value
|
||||||
|
|
||||||
|
Iterator one past the last element; identical to what [`end()`](end.md) returns.
|
||||||
|
|
||||||
|
## Exception safety
|
||||||
|
|
||||||
|
No-throw guarantee: this function never throws exceptions.
|
||||||
|
|
||||||
|
## Complexity
|
||||||
|
|
||||||
|
Constant.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example
|
||||||
|
|
||||||
|
The example below checks that every record of a batch is an object with `std::all_of`, using
|
||||||
|
[`cbegin()`](cbegin.md)/`cend()` as the range -- the same way it would for any standard container -- before
|
||||||
|
materializing any record of the batch.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__cend.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__cend.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [end](end.md) - returns an iterator to one past the last element
|
||||||
|
- [cbegin](cbegin.md) - returns a const iterator to the first element
|
||||||
|
- [`BasicJsonType::cend`](../basic_json/cend.md) - the corresponding function of `basic_json`
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
# <small>nlohmann::basic_json_view::</small>contains
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// (1)
|
||||||
|
bool contains(string_view_t key) const;
|
||||||
|
bool contains(const char* key) const;
|
||||||
|
bool contains(const string_t& key) const;
|
||||||
|
|
||||||
|
// (2)
|
||||||
|
bool contains(const json_pointer& ptr) const;
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Checks whether the value is an object with a member with key `key`.
|
||||||
|
2. Checks whether a JSON pointer `ptr` can be resolved, starting at this value.
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
`key` (in)
|
||||||
|
: key value to check its existence
|
||||||
|
|
||||||
|
`ptr` (in)
|
||||||
|
: JSON pointer to check its existence
|
||||||
|
|
||||||
|
## Return value
|
||||||
|
|
||||||
|
1. `#!cpp true` if the value is an object and has a member with key `key`, `#!cpp false` otherwise
|
||||||
|
2. `#!cpp true` if `ptr` can be resolved to a value starting at this view, `#!cpp false` otherwise
|
||||||
|
|
||||||
|
## Exception safety
|
||||||
|
|
||||||
|
No-throw guarantee: this function never throws exceptions.
|
||||||
|
|
||||||
|
## Complexity
|
||||||
|
|
||||||
|
1. Linear in the number of members: as for [`ordered_json`](../ordered_json.md), members are compared one after
|
||||||
|
another, in document order, stopping at the first match. Each comparison first checks the key's length -- already
|
||||||
|
known from the index, without reading the key bytes -- before comparing its content.
|
||||||
|
2. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
||||||
|
that level or the index into the array -- as for [`operator[]`](operator[].md#complexity) and
|
||||||
|
[`at`](at.md#complexity) with a JSON pointer.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
Overload 1 always returns `#!cpp false` when the value is not an object -- including a [discarded](is_discarded.md)
|
||||||
|
view.
|
||||||
|
|
||||||
|
!!! info "Postconditions"
|
||||||
|
|
||||||
|
If `#!cpp v.contains(key)` returns `#!cpp true`, then `#!cpp v[key]` is not [discarded](is_discarded.md). If
|
||||||
|
`#!cpp v.contains(ptr)` returns `#!cpp true`, then `#!cpp v[ptr]` is not discarded and `#!cpp v.at(ptr)` does not
|
||||||
|
throw.
|
||||||
|
|
||||||
|
!!! info "Overload 2 never throws"
|
||||||
|
|
||||||
|
Unlike [`BasicJsonType::contains(const json_pointer&)`](../basic_json/contains.md), which can throw for certain
|
||||||
|
malformed pointers (for instance an empty array-index reference token), overload 2 never throws: a missing key,
|
||||||
|
an out-of-range or malformed array index, a `#!cpp "-"` index, or a reference token used on a primitive all
|
||||||
|
simply make it return `#!cpp false`.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example "Example: (1) check with key"
|
||||||
|
|
||||||
|
The example below counts how many of a batch of records carry an optional `retry_of` field, using `contains()`
|
||||||
|
to check without ever materializing a single record of the batch.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__contains.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__contains.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
??? example "Example: (2) check with JSON pointer"
|
||||||
|
|
||||||
|
The example below checks an optional, nested field with a JSON pointer, and shows two pointers that
|
||||||
|
`#!cpp contains()` resolves to `#!cpp false` without throwing.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__contains_json_pointer.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__contains_json_pointer.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [find](find.md) - find a value in an object
|
||||||
|
- [count](count.md) - returns the number of occurrences of a key
|
||||||
|
- [at](at.md), [operator[]](operator[].md) - resolve a JSON pointer and throw, or return a discarded view
|
||||||
|
- [`BasicJsonType::contains`](../basic_json/contains.md) - the corresponding function of `basic_json`
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
# <small>nlohmann::basic_json_view::</small>count
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
size_type count(string_view_t key) const;
|
||||||
|
size_type count(const char* key) const;
|
||||||
|
size_type count(const string_t& key) const;
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns `#!cpp 1` if the value is an object with a member with key `key`, `#!cpp 0` otherwise.
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
`key` (in)
|
||||||
|
: key value of the element to count
|
||||||
|
|
||||||
|
## Return value
|
||||||
|
|
||||||
|
`#!cpp 1` if the value is an object and has a member with key `key`, `#!cpp 0` otherwise.
|
||||||
|
|
||||||
|
## Exception safety
|
||||||
|
|
||||||
|
No-throw guarantee: this function never throws exceptions.
|
||||||
|
|
||||||
|
## Complexity
|
||||||
|
|
||||||
|
Linear in the number of members: as for [`ordered_json`](../ordered_json.md), members are compared one after
|
||||||
|
another, in document order, stopping at the first match. Each comparison first checks the key's length -- already
|
||||||
|
known from the index, without reading the key bytes -- before comparing its content.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
This method always returns `#!cpp 0` when the value is not an object -- including a [discarded](is_discarded.md)
|
||||||
|
view.
|
||||||
|
|
||||||
|
Unlike [`BasicJsonType::count()`](../basic_json/count.md), whose return value can in principle exceed `#!cpp 1` for
|
||||||
|
an `ObjectType` that allows multiple entries per key, `count()` here never does: it is exactly
|
||||||
|
[`contains()`](contains.md) as `#!cpp 0`/`#!cpp 1`. This holds even if the source text has a duplicate key -- see the
|
||||||
|
[Notes on duplicate keys](operator[].md#notes) of `operator[]` -- because a `#!cpp count() > 1` result would require
|
||||||
|
counting every member with a matching key, not just finding the first one.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example
|
||||||
|
|
||||||
|
The example below validates that every transaction of a batch carries a mandatory `amount` field, using
|
||||||
|
`count()` before deciding whether to materialize a transaction at all.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__count.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__count.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [find](find.md) - find a value in an object
|
||||||
|
- [contains](contains.md) - checks whether a key exists
|
||||||
|
- [`BasicJsonType::count`](../basic_json/count.md) - the corresponding function of `basic_json`
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# <small>nlohmann::basic_json_view::</small>end
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
iterator end() const noexcept;
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns an iterator to one past the last element of an array, one past the last member value of an object, in
|
||||||
|
**document order** -- see [`begin()`](begin.md). A primitive value iterates as a range of one element (itself);
|
||||||
|
`#!json null` and a [discarded](is_discarded.md) view iterate as an empty range, so `#!cpp begin() == end()` for
|
||||||
|
them.
|
||||||
|
|
||||||
|
## Return value
|
||||||
|
|
||||||
|
Iterator one past the last element.
|
||||||
|
|
||||||
|
## Exception safety
|
||||||
|
|
||||||
|
No-throw guarantee: this function never throws exceptions.
|
||||||
|
|
||||||
|
## Complexity
|
||||||
|
|
||||||
|
Constant.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
`#!cpp iterator` is a forward iterator: unlike `BasicJsonType::iterator`, it cannot be decremented, so there is no
|
||||||
|
way to reach the last element by stepping back from `end()`. Use [`back()`](back.md) instead.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example
|
||||||
|
|
||||||
|
The example below scans a (possibly large) array of readings for the first one over a threshold, stopping the
|
||||||
|
loop at `end()` as soon as one is found. Only the matching reading, if any, is ever materialized.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__end.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__end.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [begin](begin.md) - returns an iterator to the first element
|
||||||
|
- [cend](cend.md) - returns a const iterator to one past the last element
|
||||||
|
- [`BasicJsonType::end`](../basic_json/end.md) - the corresponding function of `basic_json`
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# <small>nlohmann::basic_json_view::</small>find
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
iterator find(string_view_t key) const;
|
||||||
|
iterator find(const char* key) const;
|
||||||
|
iterator find(const string_t& key) const;
|
||||||
|
```
|
||||||
|
|
||||||
|
Finds a member with key `key` -- the first one, should the key occur more than once (see
|
||||||
|
[Notes on duplicate keys](operator[].md#notes)). If the value is not an object, or no member has this key,
|
||||||
|
[`end()`](end.md) is returned.
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
`key` (in)
|
||||||
|
: key value of the element to search for
|
||||||
|
|
||||||
|
## Return value
|
||||||
|
|
||||||
|
An iterator to the member with key `key`, or [`end()`](end.md) if there is none or the value is not an object.
|
||||||
|
|
||||||
|
## Exception safety
|
||||||
|
|
||||||
|
No-throw guarantee: this function never throws exceptions.
|
||||||
|
|
||||||
|
## Complexity
|
||||||
|
|
||||||
|
Linear in the number of members: as for [`ordered_json`](../ordered_json.md), members are compared one after
|
||||||
|
another, in document order, stopping at the first match. Each comparison first checks the key's length -- already
|
||||||
|
known from the index, without reading the key bytes -- before comparing its content.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
Unlike [`BasicJsonType::find`](../basic_json/find.md), which always returns `#!cpp end()` for a non-object type, this
|
||||||
|
also does so for a [discarded](is_discarded.md) view -- there is no separate "invalid" iterator to return.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example
|
||||||
|
|
||||||
|
The example below scans a batch of events for those that carry an optional `user_id` field, using `find()`
|
||||||
|
instead of [`operator[]`](operator[].md) (which would throw for the events that are not objects at all) or
|
||||||
|
[`contains()`](contains.md) followed by a second lookup. Events without a match are never materialized.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__find.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__find.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [count](count.md) - returns the number of occurrences of a key
|
||||||
|
- [contains](contains.md) - checks whether a key exists
|
||||||
|
- [`BasicJsonType::find`](../basic_json/find.md) - the corresponding function of `basic_json`
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# <small>nlohmann::basic_json_view::</small>front
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
basic_json_view front() const;
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns the first element of an array, the first member value of an object, or the value itself if it is primitive
|
||||||
|
(as for [`BasicJsonType::front()`](../basic_json/front.md), a primitive value is a range of one element).
|
||||||
|
|
||||||
|
## Return value
|
||||||
|
|
||||||
|
The first element or member value. For a primitive value (number, string, boolean), the value itself.
|
||||||
|
|
||||||
|
## Exception safety
|
||||||
|
|
||||||
|
Strong exception safety: if an exception is thrown, there are no changes to the view or the document it refers to.
|
||||||
|
|
||||||
|
## Exceptions
|
||||||
|
|
||||||
|
Throws [`invalid_iterator.214`](../../home/exceptions.md#jsonexceptioninvalid_iterator214) if the view is
|
||||||
|
[null](is_null.md) or [discarded](is_discarded.md), or if it is an empty array or object.
|
||||||
|
|
||||||
|
This exception does not carry a [`JSON_DIAGNOSTICS`](../macros/json_diagnostics.md) path: the view has no
|
||||||
|
`BasicJsonType` value to point at, so the exception is created without one, even if `BasicJsonType` was built with
|
||||||
|
`JSON_DIAGNOSTICS` enabled.
|
||||||
|
|
||||||
|
## Complexity
|
||||||
|
|
||||||
|
Constant.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
Unlike [`BasicJsonType::front()`](../basic_json/front.md), which has undefined behavior for an empty array or
|
||||||
|
object, `front()` throws `invalid_iterator.214` in that case -- the same way it already does for `#!json null` and
|
||||||
|
for a discarded view, where `BasicJsonType::front()` also throws.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example
|
||||||
|
|
||||||
|
The example below reads only the earliest entry of a build log with `front()`, without materializing the rest
|
||||||
|
of the (possibly long) log.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__front.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__front.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [back](back.md) - access the last element
|
||||||
|
- [`BasicJsonType::front`](../basic_json/front.md) - the corresponding function of `basic_json`
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
# <small>nlohmann::basic_json_view::</small>get
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
template<typename T>
|
||||||
|
T get() const;
|
||||||
|
```
|
||||||
|
|
||||||
|
Converts the value to `T`.
|
||||||
|
|
||||||
|
For the types below, the conversion works directly on the flat index -- no `BasicJsonType` value is built for it:
|
||||||
|
|
||||||
|
- `#!cpp bool`
|
||||||
|
- arithmetic types other than `#!cpp bool` (from a number; from a boolean, as `#!cpp 0`/`#!cpp 1`, exactly as
|
||||||
|
[`BasicJsonType::get<T>()`](../basic_json/get.md) converts a boolean)
|
||||||
|
- `#!cpp std::nullptr_t`
|
||||||
|
- `#!cpp std::basic_string<char, Traits, Alloc>` (including `string_t`) -- a copy of the string
|
||||||
|
- [`string_view_t`](index.md#member-types) -- **no copy**: the returned view points into the document's
|
||||||
|
[`source()`](../basic_json_document/source.md) text, or, for a string that contains escape sequences, into the
|
||||||
|
document's own buffer of decoded strings (see [`get_string()`](get_string.md))
|
||||||
|
- `BasicJsonType` -- equivalent to [`materialize()`](materialize.md)
|
||||||
|
- `basic_json_view` -- returns `#!cpp *this`
|
||||||
|
- `#!cpp std::vector<U, A>` -- element by element, each converted with `#!cpp get<U>()`; `#!cpp
|
||||||
|
std::vector<basic_json_view>` keeps a view of every element instead of a value
|
||||||
|
- `#!cpp std::map<K, V, C, A>` and `#!cpp std::unordered_map<K, V, H, E, A>`, if `K` is constructible from a `#!cpp
|
||||||
|
(const char*, std::size_t)` pair -- member by member, each value converted with `#!cpp get<V>()`; with a repeated
|
||||||
|
key, the *last* value is kept, as [`BasicJsonType::parse()`](../basic_json/parse.md) (and
|
||||||
|
[`materialize()`](materialize.md)) does; `#!cpp std::map<std::string, basic_json_view>` keeps views of the members
|
||||||
|
instead of values
|
||||||
|
|
||||||
|
Every other `T` -- `#!cpp std::list`, `#!cpp std::pair`, `#!cpp std::array`, enumerations, user types with a
|
||||||
|
`from_json()`, ... -- is converted by `#!cpp materialize().get<T>()`: the subtree is built into a real `BasicJsonType`
|
||||||
|
value first (as [`BasicJsonType::parse()`](../basic_json/parse.md) would), and converted from there exactly as
|
||||||
|
[`BasicJsonType::get<T>()`](../basic_json/get.md) would convert it.
|
||||||
|
|
||||||
|
## Template parameters
|
||||||
|
|
||||||
|
`T`
|
||||||
|
: the type to convert the value to
|
||||||
|
|
||||||
|
## Return value
|
||||||
|
|
||||||
|
the value, converted to `T`
|
||||||
|
|
||||||
|
## Exception safety
|
||||||
|
|
||||||
|
Strong exception safety: if an exception is thrown, there are no changes to the view or the document it refers to.
|
||||||
|
|
||||||
|
## Exceptions
|
||||||
|
|
||||||
|
- For the directly-converted types listed above (other than `BasicJsonType` and `basic_json_view`, which never
|
||||||
|
throw): throws [`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) if the value's type does not
|
||||||
|
match `T` -- the same exception, with the same message, that [`BasicJsonType::get<T>()`](../basic_json/get.md)
|
||||||
|
throws for the same JSON type and `T`.
|
||||||
|
- For `#!cpp std::vector<U, A>`: throws `type_error.302` if the value is not an array; otherwise, whatever converting
|
||||||
|
an element to `U` throws.
|
||||||
|
- For `#!cpp std::map`/`#!cpp std::unordered_map`: throws `type_error.302` if the value is not an object; otherwise,
|
||||||
|
whatever converting a member to the mapped type throws.
|
||||||
|
- For every other `T`: whatever [`materialize().get<T>()`](../basic_json/get.md) throws -- typically `type_error.302`,
|
||||||
|
or whatever a user-provided `from_json()` throws.
|
||||||
|
|
||||||
|
None of the exceptions thrown directly by this function (the first three bullets above) carry a
|
||||||
|
[`JSON_DIAGNOSTICS`](../macros/json_diagnostics.md) path: the view has no `BasicJsonType` value to point at. An
|
||||||
|
exception thrown while converting through `materialize()` (the last bullet) is different: it is thrown by a real
|
||||||
|
`BasicJsonType` value, so it **does** carry a `JSON_DIAGNOSTICS` path if `BasicJsonType` was built with it enabled.
|
||||||
|
|
||||||
|
## Complexity
|
||||||
|
|
||||||
|
- `#!cpp bool`, arithmetic types, `#!cpp std::nullptr_t`, [`string_view_t`](index.md#member-types), `basic_json_view`:
|
||||||
|
constant.
|
||||||
|
- `#!cpp std::basic_string<char, Traits, Alloc>`: constant, plus one allocation and a copy of the string's bytes.
|
||||||
|
- `BasicJsonType`: linear in the size of the subtree, see [`materialize()`](materialize.md).
|
||||||
|
- `#!cpp std::vector<U, A>`: linear in the number of elements, times the complexity of converting one element to `U`.
|
||||||
|
- `#!cpp std::map`/`#!cpp std::unordered_map`: linear in the number of members for walking them, plus the container's
|
||||||
|
own insertion cost per member (logarithmic for `#!cpp std::map`, amortized constant for `#!cpp
|
||||||
|
std::unordered_map`), times the complexity of converting one member to the mapped type.
|
||||||
|
- every other `T`: linear in the size of the subtree (building the `BasicJsonType` value), plus the complexity of
|
||||||
|
[`BasicJsonType::get<T>()`](../basic_json/get.md) on it.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
!!! info "Floating-point values"
|
||||||
|
|
||||||
|
A floating-point `T` is converted from the same digits the lexer would see during `#!cpp BasicJsonType::parse()`,
|
||||||
|
using the same conversion, so the result is bit-for-bit identical to `#!cpp BasicJsonType::parse(text).get<T>()`
|
||||||
|
for the same source text.
|
||||||
|
|
||||||
|
!!! info "Duplicate keys"
|
||||||
|
|
||||||
|
`#!cpp std::map`/`#!cpp std::unordered_map` conversions keep the *last* value of a repeated key, like
|
||||||
|
[`materialize()`](materialize.md) and [`BasicJsonType::parse()`](../basic_json/parse.md) do. This is the opposite
|
||||||
|
of [`operator[]`](operator[].md)/[`at`](at.md)/[`find`](find.md)/[`contains`](contains.md), which resolve to the
|
||||||
|
*first* occurrence (see the [Notes on duplicate keys](operator[].md#notes)).
|
||||||
|
|
||||||
|
!!! info "No pointers, references, or implicit conversion"
|
||||||
|
|
||||||
|
Unlike `BasicJsonType`, `basic_json_view` has no stored value anywhere to hand out a pointer or a reference to, so
|
||||||
|
it provides neither `#!cpp get_ptr()`, `#!cpp get_ref()`, nor `#!cpp operator ValueType()`.
|
||||||
|
[`get_string()`](get_string.md) (equivalently, `#!cpp get<string_view_t>()`) is the zero-copy alternative for
|
||||||
|
strings.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example
|
||||||
|
|
||||||
|
The example below reads typed fields straight into C++ variables, collects a view of every array element with
|
||||||
|
`#!cpp get<std::vector<basic_json_view>>()` instead of a value, and converts a nested object into a user type
|
||||||
|
through its `from_json()` -- which runs on a `BasicJsonType` value `materialize()` builds for just that one
|
||||||
|
member.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__get.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__get.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [get_to](get_to.md) - convert and write into a passed value
|
||||||
|
- [get_string](get_string.md) - the string, without a copy
|
||||||
|
- [number_token](number_token.md) - a number's token text, without a copy
|
||||||
|
- [materialize](materialize.md) - build the `BasicJsonType` value of this subtree
|
||||||
|
- [`BasicJsonType::get`](../basic_json/get.md) - the corresponding function of `basic_json`
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
# <small>nlohmann::basic_json_view::</small>get_string
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
string_view_t get_string() const;
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns the string value as a [`string_view_t`](index.md#member-types), without copying it.
|
||||||
|
|
||||||
|
## Return value
|
||||||
|
|
||||||
|
The string, as a [`string_view_t`](index.md#member-types) that points either into the document's
|
||||||
|
[`source()`](../basic_json_document/source.md) text (a string with no escape sequences), or into the document's own
|
||||||
|
buffer of decoded strings (a string that contains escape sequences, such as `#!json "\n"` or `#!json "\u00e9"`, which
|
||||||
|
had to be decoded once when the document was parsed).
|
||||||
|
|
||||||
|
## Exception safety
|
||||||
|
|
||||||
|
Strong exception safety: if an exception is thrown, there are no changes to the view or the document it refers to.
|
||||||
|
|
||||||
|
## Exceptions
|
||||||
|
|
||||||
|
Throws [`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) if the value is not a string; example:
|
||||||
|
`"type must be string, but is array"`.
|
||||||
|
|
||||||
|
This exception does not carry a [`JSON_DIAGNOSTICS`](../macros/json_diagnostics.md) path: the view has no
|
||||||
|
`BasicJsonType` value to point at, so the exception is created without one, even if `BasicJsonType` was built with
|
||||||
|
`JSON_DIAGNOSTICS` enabled.
|
||||||
|
|
||||||
|
## Complexity
|
||||||
|
|
||||||
|
Constant.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
`basic_json_view` has no `BasicJsonType` value stored anywhere, so unlike `BasicJsonType`, it has no `get_ref()` to
|
||||||
|
hand out a reference to a stored `string_t`. `get_string()` (equivalently, [`get<string_view_t>()`](get.md)) is the
|
||||||
|
zero-copy alternative: [`BasicJsonType::get_ref<const string_t&>()`](../basic_json/get_ref.md) is its closest
|
||||||
|
counterpart, except that it returns a view instead of a reference to a value that must already exist.
|
||||||
|
|
||||||
|
The returned [`string_view_t`](index.md#member-types) is valid exactly as long as the view that produced it -- see the
|
||||||
|
[validity rules](index.md) of `basic_json_view` -- and, for a string with no escapes, for as long as the document's
|
||||||
|
source text.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example
|
||||||
|
|
||||||
|
The example below pulls one field out of a JSON text that stands in for a large API response, and shows that no
|
||||||
|
`#!cpp std::string` was allocated for it: the returned view still points inside the original buffer. A field that
|
||||||
|
contains an escape sequence cannot point into the original text -- it was decoded once into the document's own
|
||||||
|
buffer instead -- but still avoids a per-field allocation.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__get_string.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__get_string.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [get](get.md) - convert the value to a given type (`#!cpp get<string_view_t>()` is equivalent to this function)
|
||||||
|
- [number_token](number_token.md) - a number's token text, without a copy
|
||||||
|
- [`BasicJsonType::get_ref`](../basic_json/get_ref.md) - the closest counterpart of `basic_json`
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# <small>nlohmann::basic_json_view::</small>get_to
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
template<typename T>
|
||||||
|
T& get_to(T& v) const;
|
||||||
|
```
|
||||||
|
|
||||||
|
Converts the value to `T` and assigns it to `v`. Equivalent to
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
v = get<T>();
|
||||||
|
return v;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Template parameters
|
||||||
|
|
||||||
|
`T`
|
||||||
|
: the type to convert the value to
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
`v` (out)
|
||||||
|
: the variable to store the converted value in
|
||||||
|
|
||||||
|
## Return value
|
||||||
|
|
||||||
|
`v`, allowing calls to chain
|
||||||
|
|
||||||
|
## Exception safety
|
||||||
|
|
||||||
|
Strong exception safety: if an exception is thrown, `v` is not modified.
|
||||||
|
|
||||||
|
## Exceptions
|
||||||
|
|
||||||
|
Whatever [`get<T>()`](get.md) throws for the same value and `T`.
|
||||||
|
|
||||||
|
## Complexity
|
||||||
|
|
||||||
|
Whatever [`get<T>()`](get.md) has for the same `T`.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example
|
||||||
|
|
||||||
|
The example below reads several fields of a service configuration directly into existing variables, then uses
|
||||||
|
the returned reference to fold the `#!cpp host`/`#!cpp port` pair into a single string in the same expression.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__get_to.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__get_to.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [get](get.md) - convert the value to a given type
|
||||||
|
- [`BasicJsonType::get_to`](../basic_json/get_to.md) - the corresponding function of `basic_json`
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -19,9 +19,12 @@ to the document and a pointer into its index), trivially copyable. A view is val
|
|||||||
Moving the document itself does not invalidate its views: the index is heap-allocated independently of the
|
Moving the document itself does not invalidate its views: the index is heap-allocated independently of the
|
||||||
`basic_json_document` object.
|
`basic_json_document` object.
|
||||||
|
|
||||||
`basic_json_view` provides the read-only part of the `BasicJsonType` interface: the type-inspection functions, and
|
`basic_json_view` provides the read-only part of the `BasicJsonType` interface: the type-inspection functions, element
|
||||||
[`materialize()`](materialize.md) to build the `BasicJsonType` value of a subtree on demand. It does not (yet) provide
|
access, lookup, iteration, and conversion -- [`get<T>()`](get.md), [`get_string()`](get_string.md),
|
||||||
element access, iteration, `get<T>()`, JSON Pointer support, `dump()`, or comparison.
|
[`number_token()`](number_token.md), and [`materialize()`](materialize.md) to build the `BasicJsonType` value of a
|
||||||
|
subtree on demand. [`operator[]`](operator%5B%5D.md), [`at`](at.md), [`contains`](contains.md), and
|
||||||
|
[`value`](value.md) also accept a [`json_pointer`](../json_pointer/index.md). It does not (yet) provide `dump()` or
|
||||||
|
comparison.
|
||||||
|
|
||||||
## Template parameters
|
## Template parameters
|
||||||
|
|
||||||
@@ -41,6 +44,9 @@ element access, iteration, `get<T>()`, JSON Pointer support, `dump()`, or compar
|
|||||||
member types of `BasicJsonType`
|
member types of `BasicJsonType`
|
||||||
- **size_type** - `#!cpp std::size_t`
|
- **size_type** - `#!cpp std::size_t`
|
||||||
- **string_view_t** - `#!cpp std::string_view` on C++17 and newer, a minimal internal substitute otherwise
|
- **string_view_t** - `#!cpp std::string_view` on C++17 and newer, a minimal internal substitute otherwise
|
||||||
|
- **iterator**, **const_iterator** - a forward iterator over the elements of an array or the member values of an
|
||||||
|
object, in document order; both names refer to the same type, since a view is always read-only
|
||||||
|
- **item** - a (key, value) pair produced by [`items()`](items.md)
|
||||||
|
|
||||||
## Member functions
|
## Member functions
|
||||||
|
|
||||||
@@ -49,6 +55,7 @@ element access, iteration, `get<T>()`, JSON Pointer support, `dump()`, or compar
|
|||||||
### Object inspection
|
### Object inspection
|
||||||
|
|
||||||
- [**type**](type.md) - return the type of the value
|
- [**type**](type.md) - return the type of the value
|
||||||
|
- [**type_name**](type_name.md) - return the type as string
|
||||||
- [**is_null**](is_null.md) - return whether the value is null
|
- [**is_null**](is_null.md) - return whether the value is null
|
||||||
- [**is_boolean**](is_boolean.md) - return whether the value is a boolean
|
- [**is_boolean**](is_boolean.md) - return whether the value is a boolean
|
||||||
- [**is_number**](is_number.md) - return whether the value is a number
|
- [**is_number**](is_number.md) - return whether the value is a number
|
||||||
@@ -64,6 +71,28 @@ element access, iteration, `get<T>()`, JSON Pointer support, `dump()`, or compar
|
|||||||
- [**is_discarded**](is_discarded.md) - return whether the view is invalid
|
- [**is_discarded**](is_discarded.md) - return whether the view is invalid
|
||||||
- [**operator bool**](operator_bool.md) - return whether the view refers to a value
|
- [**operator bool**](operator_bool.md) - return whether the view refers to a value
|
||||||
|
|
||||||
|
### Element access
|
||||||
|
|
||||||
|
- [**at**](at.md) - access specified element with bounds checking
|
||||||
|
- [**operator[]**](operator[].md) - access specified element
|
||||||
|
- [**value**](value.md) - access specified element with default value
|
||||||
|
- [**front**](front.md) - access the first element
|
||||||
|
- [**back**](back.md) - access the last element
|
||||||
|
|
||||||
|
### Lookup
|
||||||
|
|
||||||
|
- [**find**](find.md) - find an element in an object
|
||||||
|
- [**count**](count.md) - returns the number of occurrences of a key in an object
|
||||||
|
- [**contains**](contains.md) - check the existence of an element in an object
|
||||||
|
|
||||||
|
### Iterators
|
||||||
|
|
||||||
|
- [**begin**](begin.md) - returns an iterator to the first element
|
||||||
|
- [**cbegin**](cbegin.md) - returns a const iterator to the first element
|
||||||
|
- [**end**](end.md) - returns an iterator to one past the last element
|
||||||
|
- [**cend**](cend.md) - returns a const iterator to one past the last element
|
||||||
|
- [**items**](items.md) - wrapper to access iterator member functions in range-based for
|
||||||
|
|
||||||
### Capacity
|
### Capacity
|
||||||
|
|
||||||
- [**size**](size.md) - return the number of elements
|
- [**size**](size.md) - return the number of elements
|
||||||
@@ -71,6 +100,10 @@ element access, iteration, `get<T>()`, JSON Pointer support, `dump()`, or compar
|
|||||||
|
|
||||||
### Conversion
|
### Conversion
|
||||||
|
|
||||||
|
- [**get**](get.md) - get a value
|
||||||
|
- [**get_to**](get_to.md) - get a value and write it to a destination
|
||||||
|
- [**get_string**](get_string.md) - get a string value without a copy
|
||||||
|
- [**number_token**](number_token.md) - get a number's token text without a copy
|
||||||
- [**materialize**](materialize.md) - build the `BasicJsonType` value of this subtree
|
- [**materialize**](materialize.md) - build the `BasicJsonType` value of this subtree
|
||||||
|
|
||||||
### Source access
|
### Source access
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
# <small>nlohmann::basic_json_view::</small>items
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
/* unspecified */ items() const noexcept;
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns a range of [`item`](index.md#member-types) values -- (key, value) pairs -- for use in range-based for loops.
|
||||||
|
The key of an array element is its index, converted to a string, as for
|
||||||
|
[`BasicJsonType::items()`](../basic_json/items.md).
|
||||||
|
|
||||||
|
The returned type is not part of the public API and may change between versions; use a range-based for loop (see the
|
||||||
|
example), or `#!cpp decltype(v.items())` if you need to name it.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
for (const auto& item : v.items())
|
||||||
|
{
|
||||||
|
std::cout << "key: " << item.key() << ", value: " << item.value() << '\n';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
On C++17, `item` also supports [structured bindings](https://en.cppreference.com/w/cpp/language/structured_binding):
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
for (const auto [key, value] : v.items())
|
||||||
|
{
|
||||||
|
std::cout << "key: " << key << ", value: " << value << '\n';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note the `#!cpp const auto` (by value), not `#!cpp const auto&`: unlike `BasicJsonType::items()`, whose elements are
|
||||||
|
references into an existing object, a view's `item` is produced on the fly for each step of the iteration, so there
|
||||||
|
is nothing for a reference to bind to.
|
||||||
|
|
||||||
|
## Return value
|
||||||
|
|
||||||
|
A range whose iterators dereference to [`item`](index.md#member-types) and whose `#!cpp begin()`/`#!cpp end()` are
|
||||||
|
equivalent to [`basic_json_view::begin()`](begin.md)/[`end()`](end.md), in document order.
|
||||||
|
|
||||||
|
## Exception safety
|
||||||
|
|
||||||
|
No-throw guarantee: this function never throws exceptions.
|
||||||
|
|
||||||
|
## Complexity
|
||||||
|
|
||||||
|
Constant.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
As for [`begin()`](begin.md)/[`end()`](end.md), `items()` visits **every** member of an object, including all
|
||||||
|
occurrences of a duplicate key -- unlike [`operator[]`](operator[].md), [`at`](at.md), [`find`](find.md),
|
||||||
|
[`contains`](contains.md), and [`count`](count.md), which resolve to the *first* member with a given key. See the
|
||||||
|
[Notes on duplicate keys](operator[].md#notes) of `operator[]`.
|
||||||
|
|
||||||
|
!!! danger "Lifetime issues"
|
||||||
|
|
||||||
|
As for `BasicJsonType::items()`, calling `items()` on a temporary view (or a temporary document) is dangerous:
|
||||||
|
the range refers back to the document, so the document must outlive the loop. See
|
||||||
|
[#2040](https://github.com/nlohmann/json/issues/2040) for the `BasicJsonType` background.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example
|
||||||
|
|
||||||
|
The example below shows a settings object whose source text records every update to a key as a duplicate
|
||||||
|
member, in the order they happened. `items()` walks all of them, so the update history is visible, while
|
||||||
|
[`operator[]`](operator[].md) only ever sees the *first* one and [`materialize()`](materialize.md) -- like
|
||||||
|
[`BasicJsonType::parse()`](../basic_json/parse.md) -- keeps only the *last*.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__items.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__items.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [begin](begin.md), [end](end.md) - the iterators `items()` is built on
|
||||||
|
- [`BasicJsonType::items`](../basic_json/items.md) - the corresponding function of `basic_json`
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# <small>nlohmann::basic_json_view::</small>number_token
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
string_view_t number_token() const;
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns the number exactly as it appears in the source text, without parsing or rounding it.
|
||||||
|
|
||||||
|
## Return value
|
||||||
|
|
||||||
|
The number's token text, as a [`string_view_t`](index.md#member-types) into the document's
|
||||||
|
[`source()`](../basic_json_document/source.md) text -- for example `#!cpp "1.50"`, `#!cpp "1E2"`, `#!cpp "-0"`, or an
|
||||||
|
integer literal with more digits than any number type holds (such as a 30-digit integer, which [`get<T>()`](get.md)
|
||||||
|
and [`materialize()`](materialize.md) can only represent approximately, as a `number_float_t`).
|
||||||
|
|
||||||
|
## Exception safety
|
||||||
|
|
||||||
|
Strong exception safety: if an exception is thrown, there are no changes to the view or the document it refers to.
|
||||||
|
|
||||||
|
## Exceptions
|
||||||
|
|
||||||
|
Throws [`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) if the value is not a number; example:
|
||||||
|
`"type must be number, but is string"`.
|
||||||
|
|
||||||
|
This exception does not carry a [`JSON_DIAGNOSTICS`](../macros/json_diagnostics.md) path: the view has no
|
||||||
|
`BasicJsonType` value to point at, so the exception is created without one, even if `BasicJsonType` was built with
|
||||||
|
`JSON_DIAGNOSTICS` enabled.
|
||||||
|
|
||||||
|
## Complexity
|
||||||
|
|
||||||
|
Constant.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
`BasicJsonType` has no counterpart to this function: once a number is parsed into `number_integer_t`,
|
||||||
|
`number_unsigned_t`, or `number_float_t`, its original textual form (leading zeros aside, which are already rejected
|
||||||
|
by the grammar; trailing zeros in the fraction; the case and sign of the exponent; ...) is gone. `number_token()` is
|
||||||
|
useful precisely where that form must survive -- a price or an identifier that must be reproduced exactly, or a
|
||||||
|
number too large for any of `BasicJsonType`'s number types to hold without loss.
|
||||||
|
|
||||||
|
The returned [`string_view_t`](index.md#member-types) always points into the document's
|
||||||
|
[`source()`](../basic_json_document/source.md) text -- numbers are never decoded into the document's separate string
|
||||||
|
buffer -- and is valid exactly as long as that text is, see the [validity rules](index.md) of `basic_json_view`.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example
|
||||||
|
|
||||||
|
The example below keeps a price and an order ID exactly as they were written in an incoming order, where
|
||||||
|
converting them with [`get<T>()`](get.md) would lose information: the price picks up floating-point rounding, and
|
||||||
|
the order ID -- more digits than a 64-bit integer holds -- can only be approximated as a `#!cpp double` once
|
||||||
|
`#!cpp materialize()`d.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__number_token.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__number_token.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [get](get.md) - convert the value to a given type
|
||||||
|
- [get_string](get_string.md) - the string, without a copy
|
||||||
|
- [`BasicJsonType::number_integer_t`](../basic_json/number_integer_t.md),
|
||||||
|
[`number_unsigned_t`](../basic_json/number_unsigned_t.md), [`number_float_t`](../basic_json/number_float_t.md) - the
|
||||||
|
number types `#!cpp get<T>()` and `materialize()` convert into
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
# <small>nlohmann::basic_json_view::</small>operator[]
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// (1)
|
||||||
|
basic_json_view operator[](string_view_t key) const;
|
||||||
|
basic_json_view operator[](const char* key) const;
|
||||||
|
basic_json_view operator[](const string_t& key) const;
|
||||||
|
|
||||||
|
// (2)
|
||||||
|
basic_json_view operator[](size_type idx) const;
|
||||||
|
basic_json_view operator[](int idx) const;
|
||||||
|
|
||||||
|
// (3)
|
||||||
|
basic_json_view operator[](const json_pointer& ptr) const;
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Returns the value of the object member with key `key` -- the first one, should the key occur more than once (see
|
||||||
|
the [Notes](#notes) below) -- or a [discarded](is_discarded.md) view if there is no such member.
|
||||||
|
2. Returns the array element at index `idx`, or a [discarded](is_discarded.md) view if `idx` is out of range. (The
|
||||||
|
`#!cpp int` overload only exists so that an integer literal is not ambiguous between this overload and 1.)
|
||||||
|
3. Returns the value a JSON pointer `ptr` refers to, starting at this value, or a [discarded](is_discarded.md) view
|
||||||
|
wherever resolving it further is not possible without inserting into or extending the document (see
|
||||||
|
[Return value](#return-value) and [Exceptions](#exceptions) below).
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
`key` (in)
|
||||||
|
: object key of the element to access
|
||||||
|
|
||||||
|
`idx` (in)
|
||||||
|
: index of the element to access
|
||||||
|
|
||||||
|
`ptr` (in)
|
||||||
|
: JSON pointer to the element to access
|
||||||
|
|
||||||
|
## Return value
|
||||||
|
|
||||||
|
1. the value of the first member with key `key`, or a discarded view if `#!cpp is_object()` is `#!cpp false` or no
|
||||||
|
member has this key
|
||||||
|
2. the element at index `idx`, or a discarded view if `#!cpp is_array()` is `#!cpp false` or `#!cpp idx >= size()`
|
||||||
|
3. the value `ptr` resolves to, starting at this value, or a discarded view for exactly the reference tokens where the
|
||||||
|
**const** overload of [`BasicJsonType::operator[]`](../basic_json/operator%5B%5D.md) invokes undefined behavior for
|
||||||
|
the same pointer and the same document: an object member that does not exist, or an array index that is out of
|
||||||
|
range
|
||||||
|
|
||||||
|
## Exception safety
|
||||||
|
|
||||||
|
Strong exception safety: if an exception is thrown, there are no changes to the view or the document it refers to.
|
||||||
|
|
||||||
|
## Exceptions
|
||||||
|
|
||||||
|
1. Throws [`type_error.305`](../../home/exceptions.md#jsonexceptiontype_error305) if the value is not an object --
|
||||||
|
the same exception, with the same message, that the **const** overload of
|
||||||
|
[`BasicJsonType::operator[]`](../basic_json/operator%5B%5D.md) throws for a string argument on a non-object value.
|
||||||
|
2. Throws [`type_error.305`](../../home/exceptions.md#jsonexceptiontype_error305) if the value is not an array --
|
||||||
|
the same exception, with the same message, that the **const** overload of
|
||||||
|
[`BasicJsonType::operator[]`](../basic_json/operator%5B%5D.md) throws for a numeric argument on a non-array value.
|
||||||
|
3. Throws the same exceptions, with the same messages, that the **const** overload of
|
||||||
|
[`BasicJsonType::operator[]`](../basic_json/operator%5B%5D.md) throws for the same pointer and the same document:
|
||||||
|
- [`out_of_range.402`](../../home/exceptions.md#jsonexceptionout_of_range402) if a reference token is `#!cpp "-"`
|
||||||
|
at an array.
|
||||||
|
- [`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) if a reference token cannot be
|
||||||
|
resolved because it is used on a primitive value.
|
||||||
|
- [`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106) if an array index in `ptr` begins
|
||||||
|
with `#!cpp '0'`.
|
||||||
|
- [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if an array index in `ptr` is not a
|
||||||
|
number.
|
||||||
|
|
||||||
|
None of these exceptions carry a [`JSON_DIAGNOSTICS`](../macros/json_diagnostics.md) path: the view has no
|
||||||
|
`BasicJsonType` value to point at, so the exception is created without one, even if `BasicJsonType` was built with
|
||||||
|
`JSON_DIAGNOSTICS` enabled.
|
||||||
|
|
||||||
|
## Complexity
|
||||||
|
|
||||||
|
1. Linear in the number of members: as for [`ordered_json`](../ordered_json.md), members are compared one after
|
||||||
|
another, in document order, stopping at the first match. Each comparison first checks the key's length --
|
||||||
|
already known from the index, without reading the key bytes -- before comparing its content, so a key of a
|
||||||
|
different length than `key` is rejected without touching the source text.
|
||||||
|
2. Linear in `idx`: elements are skipped one at a time from the first one, since they are not a fixed size in the
|
||||||
|
index (unlike `BasicJsonType`'s array, which is random-access).
|
||||||
|
3. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
||||||
|
that level (as 1.) or the index into the array (as 2.).
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
Unlike `BasicJsonType::operator[]`, which is undefined behavior (guarded by a
|
||||||
|
[runtime assertion](../../features/assertions.md)) for a missing key on a **const** value, this operator always
|
||||||
|
returns a safe, testable result: a [discarded](is_discarded.md) view, which is `#!cpp false` in a boolean context.
|
||||||
|
There is also no non-const overload that inserts a missing key or extends an array -- a view never modifies the
|
||||||
|
document.
|
||||||
|
|
||||||
|
!!! info "Duplicate keys"
|
||||||
|
|
||||||
|
If the source text has an object with a duplicate key, `#!cpp operator[]` (and [`at`](at.md), [`find`](find.md),
|
||||||
|
[`contains`](contains.md), [`count`](count.md)) all resolve to the *first* member with that key, because a
|
||||||
|
lookup can stop as soon as it finds a match. This is different from
|
||||||
|
[`materialize()`](materialize.md) (and [`BasicJsonType::parse()`](../basic_json/parse.md)), which replay every
|
||||||
|
member in order and so end up keeping the *last* value for a repeated key -- there is no reason for them to stop
|
||||||
|
early. [`begin()`](begin.md)/[`end()`](end.md) and [`items()`](items.md) iterate over *all* members, including
|
||||||
|
duplicates, in document order. See the example below and [`size()`](size.md#notes).
|
||||||
|
|
||||||
|
!!! info "JSON pointer resolution"
|
||||||
|
|
||||||
|
Overload 3 walks `ptr` one reference token at a time, starting at this value, the same way [`at`](at.md) and
|
||||||
|
[`contains`](contains.md) do. It only ever returns a discarded view where the **const** overload of
|
||||||
|
`BasicJsonType::operator[]` would be undefined behavior for the same pointer -- a missing object member or an
|
||||||
|
out-of-range array index -- and still throws for every other way `ptr` can fail to resolve. See
|
||||||
|
[`at`](at.md#exceptions) for the checked version, which throws in every case instead, and
|
||||||
|
[`contains`](contains.md) for a version that never throws.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example "Example: (1)/(2) access specified element"
|
||||||
|
|
||||||
|
The example below reads a couple of fields out of a batch of user records without ever materializing a full
|
||||||
|
`BasicJsonType` value for the batch. `operator[]` is used both to look up an optional object member and to index
|
||||||
|
into an array -- in both cases, a missing value comes back as a discarded view that can be tested with a plain
|
||||||
|
`#!cpp if`, instead of relying on undefined behavior.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__operator[].cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__operator[].output"
|
||||||
|
```
|
||||||
|
|
||||||
|
??? example "Example: (3) access specified element via JSON pointer"
|
||||||
|
|
||||||
|
The example below reaches straight into one deeply nested field of a large document with a single JSON pointer,
|
||||||
|
without ever building a tree for the rest of it, and shows the discarded-view and throwing outcomes of a pointer
|
||||||
|
that cannot be fully resolved.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__operator[]_json_pointer.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__operator[]_json_pointer.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [at](at.md) - access specified element with bounds checking (throws instead of returning a discarded view)
|
||||||
|
- [front](front.md), [back](back.md) - access the first or last element
|
||||||
|
- [find](find.md), [contains](contains.md) - look up a member without throwing
|
||||||
|
- [`BasicJsonType::operator[]`](../basic_json/operator%5B%5D.md) - the corresponding function of `basic_json`
|
||||||
|
- [`json_pointer`](../json_pointer/index.md) - JSON pointer type used by overload 3
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -33,6 +33,12 @@ Constant: for an object or array, the element count is stored in the index, not
|
|||||||
As for [`BasicJsonType::size()`](../basic_json/size.md), this does not return the length of a string value -- it is
|
As for [`BasicJsonType::size()`](../basic_json/size.md), this does not return the length of a string value -- it is
|
||||||
`1` for a string, regardless of its length.
|
`1` for a string, regardless of its length.
|
||||||
|
|
||||||
|
If the source text has an object with a duplicate key, every occurrence counts towards its `size()` -- unlike
|
||||||
|
[`materialize()`](materialize.md) (and [`BasicJsonType::parse()`](../basic_json/parse.md)), which keeps only the last
|
||||||
|
value for a repeated key. This means `#!cpp v.size()` can be larger than `#!cpp v.materialize().size()`. See the
|
||||||
|
[Notes on duplicate keys](operator[].md#notes) of `operator[]` for why lookups and iteration disagree on how many
|
||||||
|
members there are.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# <small>nlohmann::basic_json_view::</small>type_name
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
const char* type_name() const noexcept;
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns the type name as string to be used in error messages -- usually to indicate that a function was called on a
|
||||||
|
wrong JSON type. Identical to [`BasicJsonType::type_name()`](../basic_json/type_name.md), including the extra
|
||||||
|
`#!cpp "discarded"` return value for a [discarded](is_discarded.md) view (`BasicJsonType::type_name()` produces the
|
||||||
|
same string for a discarded `BasicJsonType` value).
|
||||||
|
|
||||||
|
## Return value
|
||||||
|
|
||||||
|
a string representation of the type ([`value_t`](../basic_json/value_t.md)):
|
||||||
|
|
||||||
|
| Value type | return value |
|
||||||
|
|-----------------------------------------------------|---------------|
|
||||||
|
| `#!json null` | `"null"` |
|
||||||
|
| boolean | `"boolean"` |
|
||||||
|
| string | `"string"` |
|
||||||
|
| number (integer, unsigned integer, floating-point) | `"number"` |
|
||||||
|
| object | `"object"` |
|
||||||
|
| array | `"array"` |
|
||||||
|
| discarded | `"discarded"` |
|
||||||
|
|
||||||
|
`type_name()` never returns `#!cpp "binary"`, since a JSON text has no binary values (see
|
||||||
|
[`is_binary()`](is_binary.md)); it also never returns `#!cpp "invalid"`, since a view's `#!cpp kind` always comes
|
||||||
|
from a value the parser actually produced.
|
||||||
|
|
||||||
|
## Exception safety
|
||||||
|
|
||||||
|
No-throw guarantee: this function never throws exceptions.
|
||||||
|
|
||||||
|
## Complexity
|
||||||
|
|
||||||
|
Constant.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example
|
||||||
|
|
||||||
|
The example below reports why some parsed messages were rejected, using only `type_name()` -- no
|
||||||
|
`BasicJsonType` value is ever built for the ones that are wrong, and the message text matches what
|
||||||
|
[`BasicJsonType::type_name()`](../basic_json/type_name.md) would produce for the same value.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__type_name.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__type_name.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [type](type.md) - return the type of the value
|
||||||
|
- [`BasicJsonType::type_name`](../basic_json/type_name.md) - the corresponding function of `basic_json`
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
# <small>nlohmann::basic_json_view::</small>value
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// (1)
|
||||||
|
template<typename T>
|
||||||
|
T value(string_view_t key, const T& default_value) const;
|
||||||
|
string_t value(string_view_t key, const char* default_value) const;
|
||||||
|
|
||||||
|
// (2)
|
||||||
|
template<typename T>
|
||||||
|
T value(const json_pointer& ptr, const T& default_value) const;
|
||||||
|
string_t value(const json_pointer& ptr, const char* default_value) const;
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Returns the value of the object member with key `key` -- the first one, should the key occur more than once (see
|
||||||
|
[Notes on duplicate keys](operator[].md#notes)) -- converted to `T`, or `default_value` if there is no such member.
|
||||||
|
2. Returns the value a JSON pointer `ptr` refers to, starting at this value, converted to `T`, or `default_value` if
|
||||||
|
`ptr` cannot be resolved.
|
||||||
|
|
||||||
|
Both overloads have a dedicated `#!cpp const char*` overload, so `#!cpp v.value(key, "default")` (and the JSON pointer
|
||||||
|
equivalent) deduce `string_t`, not `const char*`, for their return type and for the comparison used to pick between
|
||||||
|
`key` and `default_value`.
|
||||||
|
|
||||||
|
## Template parameters
|
||||||
|
|
||||||
|
`T`
|
||||||
|
: the type to convert the found value to; also the type of `default_value`
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
`key` (in)
|
||||||
|
: object key of the element to access
|
||||||
|
|
||||||
|
`ptr` (in)
|
||||||
|
: JSON pointer to the element to access
|
||||||
|
|
||||||
|
`default_value` (in)
|
||||||
|
: the value to return if `key`/`ptr` resolves to no value
|
||||||
|
|
||||||
|
## Return value
|
||||||
|
|
||||||
|
1. the first member with key `key`, converted to `T`, or `default_value`
|
||||||
|
2. the value `ptr` resolves to, converted to `T`, or `default_value`
|
||||||
|
|
||||||
|
## Exception safety
|
||||||
|
|
||||||
|
Strong exception safety: if an exception is thrown, there are no changes to the view or the document it refers to.
|
||||||
|
|
||||||
|
## Exceptions
|
||||||
|
|
||||||
|
1. Throws [`type_error.306`](../../home/exceptions.md#jsonexceptiontype_error306) if the value is not an object --
|
||||||
|
the same exception, with the same message, that [`BasicJsonType::value`](../basic_json/value.md) throws for the
|
||||||
|
same call. If a member with key `key` is found, throws whatever converting it to `T` throws (typically
|
||||||
|
[`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302), with the same message
|
||||||
|
[`BasicJsonType::value`](../basic_json/value.md) throws for the same mismatch); a missing member never throws.
|
||||||
|
2. Throws [`type_error.306`](../../home/exceptions.md#jsonexceptiontype_error306) if this value -- not the value `ptr`
|
||||||
|
resolves to -- is neither an object nor an array. Throws [`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106)
|
||||||
|
or [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if `ptr` contains a malformed array
|
||||||
|
index. If `ptr` resolves to a value, throws whatever converting it to `T` throws. Every other way `ptr` can fail to
|
||||||
|
resolve -- a missing key, an out-of-range or "`-`" array index, an unresolvable token on a primitive -- yields
|
||||||
|
`default_value` instead of throwing, exactly as [`BasicJsonType::value`](../basic_json/value.md) catches
|
||||||
|
`out_of_range` and returns `default_value`.
|
||||||
|
|
||||||
|
None of these exceptions carry a [`JSON_DIAGNOSTICS`](../macros/json_diagnostics.md) path: the view has no
|
||||||
|
`BasicJsonType` value to point at, so the exception is created without one, even if `BasicJsonType` was built with
|
||||||
|
`JSON_DIAGNOSTICS` enabled.
|
||||||
|
|
||||||
|
## Complexity
|
||||||
|
|
||||||
|
1. Linear in the number of members: as for [`operator[]`](operator[].md#complexity), members are compared one after
|
||||||
|
another, in document order, stopping at the first match. Plus the complexity of converting the found member to
|
||||||
|
`T` (see [`get`](get.md)).
|
||||||
|
2. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
||||||
|
that level or the index into the array -- as for the [`operator[]`](operator[].md#complexity) and
|
||||||
|
[`at`](at.md#complexity) overloads that take a JSON pointer. Plus the complexity of converting the resolved value
|
||||||
|
to `T`.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
!!! info "Differences to `at` and `operator[]`"
|
||||||
|
|
||||||
|
Unlike [`at`](at.md), this function does not throw if `key`/`ptr` resolves to no value. Unlike
|
||||||
|
[`operator[]`](operator[].md), it never returns a [discarded](is_discarded.md) view -- it always returns a `T` --
|
||||||
|
and it is available on any view, since it never needs to insert a missing element the way the non-const
|
||||||
|
`BasicJsonType::operator[]` would.
|
||||||
|
|
||||||
|
!!! info "Which values can be asked"
|
||||||
|
|
||||||
|
As for [`BasicJsonType::value`](../basic_json/value.md), the key overload (1) requires an object, and the JSON
|
||||||
|
pointer overload (2) an object or an array.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example "Example: (1) access specified object element with default value"
|
||||||
|
|
||||||
|
The example below reads a couple of optional configuration fields with a default, so that a missing key never
|
||||||
|
needs a `#!cpp try`/`#!cpp catch` of its own.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__value.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__value.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
??? example "Example: (2) access specified element via JSON pointer with default value"
|
||||||
|
|
||||||
|
The example below reads an optional, nested configuration value with a default, given as a JSON pointer.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__value_json_pointer.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__value_json_pointer.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [at](at.md) - access specified element with bounds checking (throws instead of returning a default value)
|
||||||
|
- [operator[]](operator[].md) - access specified element (returns a discarded view instead of a default value)
|
||||||
|
- [`BasicJsonType::value`](../basic_json/value.md) - the corresponding function of `basic_json`
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
using json_view = nlohmann::json_view;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// required fields of a service configuration -- at() reports a missing
|
||||||
|
// or wrong-typed field with the very same exception basic_json::at()
|
||||||
|
// would throw for the equivalent nlohmann::json value, so error
|
||||||
|
// handling written against basic_json::at() keeps working unchanged
|
||||||
|
json_document config = json_document::parse(R"({"name": "cache", "port": "6379"})");
|
||||||
|
const json_view service = config.root();
|
||||||
|
|
||||||
|
std::cout << service.at("port").materialize().dump() << '\n';
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// "port" is a string, not an array
|
||||||
|
static_cast<void>(service.at("port").at(0));
|
||||||
|
}
|
||||||
|
catch (const nlohmann::json::type_error& e)
|
||||||
|
{
|
||||||
|
std::cout << e.what() << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
static_cast<void>(service.at("timeout"));
|
||||||
|
}
|
||||||
|
catch (const nlohmann::json::out_of_range& e)
|
||||||
|
{
|
||||||
|
std::cout << e.what() << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
"6379"
|
||||||
|
[json.exception.type_error.304] cannot use at() with string
|
||||||
|
[json.exception.out_of_range.403] key 'timeout' not found
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
using json_pointer = nlohmann::json::json_pointer;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
json_document doc = json_document::parse(R"({"region": "eu", "servers": ["eu-1", "eu-2"]})");
|
||||||
|
const auto root = doc.root();
|
||||||
|
|
||||||
|
std::cout << root.at(json_pointer("/servers/1")).materialize().dump() << '\n';
|
||||||
|
|
||||||
|
// at() throws for every resolution failure -- with the very same
|
||||||
|
// message json::at(ptr) would throw for the same pointer and the same
|
||||||
|
// document
|
||||||
|
try
|
||||||
|
{
|
||||||
|
static_cast<void>(root.at(json_pointer("/servers/5")));
|
||||||
|
}
|
||||||
|
catch (const nlohmann::json::out_of_range& e)
|
||||||
|
{
|
||||||
|
std::cout << e.what() << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
static_cast<void>(root.at(json_pointer("/missing")));
|
||||||
|
}
|
||||||
|
catch (const nlohmann::json::out_of_range& e)
|
||||||
|
{
|
||||||
|
std::cout << e.what() << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
"eu-2"
|
||||||
|
[json.exception.out_of_range.401] array index 5 is out of range
|
||||||
|
[json.exception.out_of_range.403] key 'missing' not found
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// the same build log; back() reads only the final status. It is linear
|
||||||
|
// in the number of events (unlike front(), which is constant), but
|
||||||
|
// still far less work than materializing the whole array
|
||||||
|
json_document log = json_document::parse(R"(["queued", "started", "compiling", "linking", "done"])");
|
||||||
|
std::cout << log.root().back().materialize().dump() << '\n';
|
||||||
|
|
||||||
|
// an empty log -- back() throws instead of the undefined behavior
|
||||||
|
// basic_json::back() has for an empty array
|
||||||
|
json_document empty_log = json_document::parse("[]");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
static_cast<void>(empty_log.root().back());
|
||||||
|
}
|
||||||
|
catch (const nlohmann::json::invalid_iterator& e)
|
||||||
|
{
|
||||||
|
std::cout << e.what() << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
"done"
|
||||||
|
[json.exception.invalid_iterator.214] cannot get value
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// a log record: the fields matter in the order they were written, e.g.
|
||||||
|
// to reproduce the record as it was logged. A nlohmann::json object
|
||||||
|
// sorts its keys, so materializing and iterating it would instead print
|
||||||
|
// them alphabetically ("level", "message", "time")
|
||||||
|
json_document record = json_document::parse(R"({"time": "10:00:01", "level": "info", "message": "started"})");
|
||||||
|
|
||||||
|
for (auto it = record.root().begin(); it != record.root().end(); ++it)
|
||||||
|
{
|
||||||
|
std::cout << it.key() << '=' << it->materialize().dump() << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
time="10:00:01"
|
||||||
|
level="info"
|
||||||
|
message="started"
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <numeric>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
using json_view = nlohmann::json_view;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// sum many measurements with std::accumulate; cbegin()/cend() (identical
|
||||||
|
// to begin()/end() here -- the view is always read-only) let the view be
|
||||||
|
// used with standard algorithms without ever materializing the whole
|
||||||
|
// array into a nlohmann::json value
|
||||||
|
json_document measurements = json_document::parse("[3, 1, 4, 1, 5, 9, 2, 6]");
|
||||||
|
const auto values = measurements.root();
|
||||||
|
|
||||||
|
const int sum = std::accumulate(values.cbegin(), values.cend(), 0,
|
||||||
|
[](int total, const json_view & v)
|
||||||
|
{
|
||||||
|
return total + v.materialize().get<int>();
|
||||||
|
});
|
||||||
|
|
||||||
|
std::cout << sum << '\n';
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
31
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
using json_view = nlohmann::json_view;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// check that every element of a (possibly large) batch is an object,
|
||||||
|
// before materializing any of them -- cbegin()/cend() (identical to
|
||||||
|
// begin()/end() here) work as the range for std::all_of like they would
|
||||||
|
// for any standard container
|
||||||
|
json_document batch = json_document::parse(R"([{"id": 1}, {"id": 2}, {"id": 3}])");
|
||||||
|
const auto records = batch.root();
|
||||||
|
|
||||||
|
const bool all_objects = std::all_of(records.cbegin(), records.cend(),
|
||||||
|
[](const json_view & v)
|
||||||
|
{
|
||||||
|
return v.is_object();
|
||||||
|
});
|
||||||
|
|
||||||
|
std::cout << std::boolalpha << all_objects << '\n';
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
true
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// count how many of many incoming records carry an optional "retry_of"
|
||||||
|
// field -- contains() only walks the flat index, so scanning a large
|
||||||
|
// batch like this never builds a single nlohmann::json value
|
||||||
|
json_document batch = json_document::parse(R"(
|
||||||
|
[
|
||||||
|
{"id": 1},
|
||||||
|
{"id": 2, "retry_of": 1},
|
||||||
|
{"id": 3},
|
||||||
|
{"id": 4, "retry_of": 3}
|
||||||
|
]
|
||||||
|
)");
|
||||||
|
|
||||||
|
const auto records = batch.root();
|
||||||
|
std::size_t retries = 0;
|
||||||
|
for (std::size_t i = 0; i < records.size(); ++i)
|
||||||
|
{
|
||||||
|
if (records[i].contains("retry_of"))
|
||||||
|
{
|
||||||
|
++retries;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::cout << retries << " of " << records.size() << " records are retries\n";
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
2 of 4 records are retries
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
using json_pointer = nlohmann::json::json_pointer;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// "retry_of" is only present on some records, nested a level down
|
||||||
|
// inside "meta"
|
||||||
|
json_document batch = json_document::parse(R"(
|
||||||
|
[
|
||||||
|
{"id": 1, "meta": {}},
|
||||||
|
{"id": 2, "meta": {"retry_of": 1}}
|
||||||
|
]
|
||||||
|
)");
|
||||||
|
|
||||||
|
const auto records = batch.root();
|
||||||
|
const json_pointer retry_of("/meta/retry_of");
|
||||||
|
for (std::size_t i = 0; i < records.size(); ++i)
|
||||||
|
{
|
||||||
|
const auto record = records[i];
|
||||||
|
if (record.contains(retry_of))
|
||||||
|
{
|
||||||
|
std::cout << "record " << i << " is a retry of " << record[retry_of].get<int>() << '\n';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << "record " << i << " is original\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// contains() with a JSON pointer never throws -- not even for a
|
||||||
|
// pointer that indexes into a primitive ("/0/id/x") or uses a
|
||||||
|
// malformed array index ("/01"), either of which would need a
|
||||||
|
// try/catch with json::contains(ptr)
|
||||||
|
std::cout << std::boolalpha << records.contains(json_pointer("/0/id/x")) << '\n';
|
||||||
|
std::cout << std::boolalpha << records.contains(json_pointer("/01")) << '\n';
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
record 0 is original
|
||||||
|
record 1 is a retry of 1
|
||||||
|
false
|
||||||
|
false
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// validate that every transaction of a batch carries a mandatory
|
||||||
|
// "amount" field before materializing any of them into a nlohmann::json
|
||||||
|
// value -- count() returns 0 or 1 for an object
|
||||||
|
json_document batch = json_document::parse(R"(
|
||||||
|
[
|
||||||
|
{"id": 1, "amount": 9.99},
|
||||||
|
{"id": 2}
|
||||||
|
]
|
||||||
|
)");
|
||||||
|
|
||||||
|
const auto transactions = batch.root();
|
||||||
|
for (std::size_t i = 0; i < transactions.size(); ++i)
|
||||||
|
{
|
||||||
|
const auto transaction = transactions[i];
|
||||||
|
if (transaction.count("amount") == 0)
|
||||||
|
{
|
||||||
|
std::cout << "transaction " << i << " is missing \"amount\"\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::cout << "transaction " << i << ": " << transaction["amount"].materialize().dump() << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
transaction 0: 9.99
|
||||||
|
transaction 1 is missing "amount"
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// scan a (possibly large) array of readings for the first one over a
|
||||||
|
// threshold; the loop stops at end() as soon as one is found, and only
|
||||||
|
// the matching reading is ever materialized
|
||||||
|
json_document readings = json_document::parse("[12, 18, 25, 31, 9]");
|
||||||
|
const auto values = readings.root();
|
||||||
|
|
||||||
|
auto it = values.begin();
|
||||||
|
for (; it != values.end(); ++it)
|
||||||
|
{
|
||||||
|
if (it->materialize().get<int>() > 20)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it != values.end())
|
||||||
|
{
|
||||||
|
std::cout << "first reading over 20: " << it->materialize().dump() << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
first reading over 20: 25
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// a batch of incoming events; only some carry a "user_id" -- find()
|
||||||
|
// locates it without throwing for the events that turn out to not be
|
||||||
|
// objects, and without materializing an event that does not match
|
||||||
|
json_document batch = json_document::parse(R"(
|
||||||
|
[
|
||||||
|
{"type": "click", "user_id": 42},
|
||||||
|
{"type": "ping"},
|
||||||
|
{"type": "click", "user_id": 7}
|
||||||
|
]
|
||||||
|
)");
|
||||||
|
|
||||||
|
const auto events = batch.root();
|
||||||
|
for (std::size_t i = 0; i < events.size(); ++i)
|
||||||
|
{
|
||||||
|
const auto event = events[i];
|
||||||
|
const auto it = event.find("user_id");
|
||||||
|
if (it != event.end())
|
||||||
|
{
|
||||||
|
std::cout << "user " << it->materialize().dump() << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
user 42
|
||||||
|
user 7
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// the build log of a running job; front() reads just the earliest event
|
||||||
|
// without materializing the (possibly long) rest of the log
|
||||||
|
json_document log = json_document::parse(R"(["queued", "started", "compiling", "linking", "done"])");
|
||||||
|
std::cout << log.root().front().materialize().dump() << '\n';
|
||||||
|
|
||||||
|
// an empty log -- front() throws instead of the undefined behavior
|
||||||
|
// basic_json::front() has for an empty array
|
||||||
|
json_document empty_log = json_document::parse("[]");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
static_cast<void>(empty_log.root().front());
|
||||||
|
}
|
||||||
|
catch (const nlohmann::json::invalid_iterator& e)
|
||||||
|
{
|
||||||
|
std::cout << e.what() << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
"queued"
|
||||||
|
[json.exception.invalid_iterator.214] cannot get value
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
using json_view = nlohmann::json_view;
|
||||||
|
|
||||||
|
// address has no direct conversion in get<T>(), so get<address>() falls back
|
||||||
|
// to materialize().get<address>() -- a real nlohmann::json value is built
|
||||||
|
// for just this one member, and its own from_json() runs on that
|
||||||
|
struct address
|
||||||
|
{
|
||||||
|
std::string city;
|
||||||
|
int zip = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void from_json(const nlohmann::json& j, address& a)
|
||||||
|
{
|
||||||
|
j.at("city").get_to(a.city);
|
||||||
|
j.at("zip").get_to(a.zip);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
json_document doc = json_document::parse(R"(
|
||||||
|
{
|
||||||
|
"name": "Alice",
|
||||||
|
"active": true,
|
||||||
|
"orders": [1, 2, 3],
|
||||||
|
"address": {"city": "Berlin", "zip": 10115}
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
const json_view customer = doc.root();
|
||||||
|
|
||||||
|
// read typed fields straight into C++ variables -- none of these build
|
||||||
|
// a nlohmann::json value
|
||||||
|
const std::string name = customer["name"].get<std::string>();
|
||||||
|
const bool active = customer["active"].get<bool>();
|
||||||
|
std::cout << name << (active ? " (active)" : " (inactive)") << '\n';
|
||||||
|
|
||||||
|
// std::vector<json_view> keeps views of the array elements instead of
|
||||||
|
// copies of their values
|
||||||
|
bool first = true;
|
||||||
|
for (const json_view order : customer["orders"].get<std::vector<json_view>>())
|
||||||
|
{
|
||||||
|
std::cout << (first ? "" : " ") << order.get<int>();
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
std::cout << '\n';
|
||||||
|
|
||||||
|
// everything else goes through materialize()
|
||||||
|
const address a = customer["address"].get<address>();
|
||||||
|
std::cout << a.city << ' ' << a.zip << '\n';
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
Alice (active)
|
||||||
|
1 2 3
|
||||||
|
Berlin 10115
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
using json_view = nlohmann::json_view;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// a "large response" stand-in: only the "id" field is ever read out of it
|
||||||
|
const std::string text =
|
||||||
|
R"({"id": "8f14e45f-ceea-467e-bb92-963f5e3c7a08", "note": "created via API\n", "payload": "..."})";
|
||||||
|
const json_document doc = json_document::parse(text);
|
||||||
|
const json_view response = doc.root();
|
||||||
|
|
||||||
|
const json_view::string_view_t id = response["id"].get_string();
|
||||||
|
std::cout << id << '\n';
|
||||||
|
|
||||||
|
// no std::string was allocated for "id": its bytes still live inside
|
||||||
|
// the original buffer, so id's data lies inside [text.data(),
|
||||||
|
// text.data() + text.size())
|
||||||
|
const bool id_in_source = id.data() >= text.data() && id.data() + id.size() <= text.data() + text.size();
|
||||||
|
std::cout << std::boolalpha << id_in_source << '\n';
|
||||||
|
|
||||||
|
// "note" contains an escape sequence ('\n'), so it was decoded once
|
||||||
|
// into the document's own buffer -- get_string() still avoids a copy
|
||||||
|
// into a new std::string, but the bytes no longer live inside "text"
|
||||||
|
const json_view::string_view_t note = response["note"].get_string();
|
||||||
|
const bool note_in_source = note.data() >= text.data() && note.data() + note.size() <= text.data() + text.size();
|
||||||
|
std::cout << std::boolalpha << note_in_source << '\n';
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
8f14e45f-ceea-467e-bb92-963f5e3c7a08
|
||||||
|
true
|
||||||
|
false
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
using json_view = nlohmann::json_view;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
json_document doc = json_document::parse(R"({"host": "db.example.com", "port": 5432, "ssl": true})");
|
||||||
|
const json_view config = doc.root();
|
||||||
|
|
||||||
|
// get_to() writes directly into existing variables -- handy for filling
|
||||||
|
// in the members of a struct one field at a time, without an
|
||||||
|
// intermediate value from get<T>() for each one
|
||||||
|
std::string host;
|
||||||
|
int port = 0;
|
||||||
|
bool ssl = false;
|
||||||
|
config["host"].get_to(host);
|
||||||
|
config["port"].get_to(port);
|
||||||
|
config["ssl"].get_to(ssl);
|
||||||
|
std::cout << host << ':' << port << (ssl ? " (tls)" : "") << '\n';
|
||||||
|
|
||||||
|
// the return value is a reference to the argument, so a call can be
|
||||||
|
// used directly in a larger expression
|
||||||
|
std::string other_host;
|
||||||
|
std::cout << config["host"].get_to(other_host).size() << '\n';
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
db.example.com:5432 (tls)
|
||||||
|
14
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// a settings object whose source text records every update to a key as
|
||||||
|
// a duplicate member. items() visits all of them, in document order, so
|
||||||
|
// the update history is visible; operator[] only ever sees the first
|
||||||
|
// one, and materialize() -- like basic_json::parse() -- keeps the last
|
||||||
|
json_document updates = json_document::parse(R"({"retries": 1, "timeout": 30, "retries": 5})");
|
||||||
|
const auto settings = updates.root();
|
||||||
|
|
||||||
|
for (const auto& item : settings.items())
|
||||||
|
{
|
||||||
|
std::cout << item.key() << '=' << item.value().materialize().dump() << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "first \"retries\" seen by operator[]: " << settings["retries"].materialize().dump() << '\n';
|
||||||
|
std::cout << "last \"retries\" kept by materialize(): " << settings.materialize()["retries"].dump() << '\n';
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
retries=1
|
||||||
|
timeout=30
|
||||||
|
retries=5
|
||||||
|
first "retries" seen by operator[]: 1
|
||||||
|
last "retries" kept by materialize(): 5
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
using json_view = nlohmann::json_view;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// a price and an order id from an incoming order -- both need to be
|
||||||
|
// reproduced exactly, e.g. for an invoice or an audit log
|
||||||
|
json_document doc = json_document::parse(R"(
|
||||||
|
{"price": 19.90, "order_id": 1234567890123456789012345, "quantity": 3}
|
||||||
|
)");
|
||||||
|
const json_view order = doc.root();
|
||||||
|
|
||||||
|
// number_token() returns the number exactly as written in the source
|
||||||
|
std::cout << order["price"].number_token() << '\n';
|
||||||
|
std::cout << order["order_id"].number_token() << '\n';
|
||||||
|
|
||||||
|
// get<double>() converts it instead -- the exact source text is gone:
|
||||||
|
// "19.90" becomes the double closest to 19.9, printed without the
|
||||||
|
// trailing zero, and the 25-digit order id -- far beyond any 64-bit
|
||||||
|
// integer -- can only be approximated as a double
|
||||||
|
std::cout << order["price"].get<double>() << '\n';
|
||||||
|
std::cout << order.materialize()["order_id"].dump() << '\n';
|
||||||
|
|
||||||
|
// an ordinary quantity has nothing to lose either way
|
||||||
|
std::cout << order["quantity"].number_token() << " == " << order["quantity"].get<int>() << '\n';
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
19.90
|
||||||
|
1234567890123456789012345
|
||||||
|
19.9
|
||||||
|
1.2345678901234568e+24
|
||||||
|
3 == 3
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// two user records from a large API response; only the fields that are
|
||||||
|
// actually read are ever touched, and no nlohmann::json tree is built
|
||||||
|
// for the batch
|
||||||
|
json_document batch = json_document::parse(R"(
|
||||||
|
[
|
||||||
|
{"name": "Alice", "email": "alice@example.com", "tags": ["admin", "ops"]},
|
||||||
|
{"name": "Bob", "tags": []}
|
||||||
|
]
|
||||||
|
)");
|
||||||
|
|
||||||
|
const auto users = batch.root();
|
||||||
|
for (std::size_t i = 0; i < users.size(); ++i)
|
||||||
|
{
|
||||||
|
const auto user = users[i];
|
||||||
|
std::cout << user["name"].materialize().dump();
|
||||||
|
|
||||||
|
// operator[] on a missing object key gives a discarded view -- test
|
||||||
|
// it with a plain "if". The const overload of json::operator[]
|
||||||
|
// would instead be undefined behavior (guarded by an assertion) for
|
||||||
|
// a missing key
|
||||||
|
if (const auto email = user["email"])
|
||||||
|
{
|
||||||
|
std::cout << " <" << email.materialize().dump() << ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
// the same holds for an array index past the end: a discarded view,
|
||||||
|
// not undefined behavior
|
||||||
|
if (const auto first_tag = user["tags"][0])
|
||||||
|
{
|
||||||
|
std::cout << " #" << first_tag.materialize().dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
"Alice" <"alice@example.com"> #"admin"
|
||||||
|
"Bob"
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
using json_pointer = nlohmann::json::json_pointer;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// a larger document; operator[] with a JSON pointer reaches straight to
|
||||||
|
// one deeply nested field, without ever building a tree for the rest
|
||||||
|
json_document doc = json_document::parse(R"(
|
||||||
|
{
|
||||||
|
"region": {
|
||||||
|
"servers": [
|
||||||
|
{"name": "eu-1", "metrics": {"cpu": 0.42}},
|
||||||
|
{"name": "eu-2", "metrics": {"cpu": 0.71}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
|
||||||
|
const auto root = doc.root();
|
||||||
|
std::cout << root[json_pointer("/region/servers/1/metrics/cpu")].materialize().dump() << '\n';
|
||||||
|
|
||||||
|
// a missing key or an out-of-range index along the path gives a
|
||||||
|
// discarded view, exactly where const json::operator[] would be
|
||||||
|
// undefined behavior for the same pointer
|
||||||
|
if (const auto missing = root[json_pointer("/region/servers/5/metrics/cpu")])
|
||||||
|
{
|
||||||
|
std::cout << missing.materialize().dump() << '\n';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << "no such server\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// indexing into a primitive still throws, as basic_json::operator[]
|
||||||
|
// does for the same pointer
|
||||||
|
try
|
||||||
|
{
|
||||||
|
static_cast<void>(root[json_pointer("/region/servers/0/name/x")]);
|
||||||
|
}
|
||||||
|
catch (const nlohmann::json::out_of_range& e)
|
||||||
|
{
|
||||||
|
std::cout << e.what() << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
0.71
|
||||||
|
no such server
|
||||||
|
[json.exception.out_of_range.404] unresolved reference token 'x'
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
using json_view = nlohmann::json_view;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// report why some parsed messages were rejected, using only
|
||||||
|
// type_name() -- no nlohmann::json value is built for the ones that
|
||||||
|
// are wrong
|
||||||
|
json_document good = json_document::parse(R"({"id": 1})");
|
||||||
|
json_document bad = json_document::parse("[1, 2, 3]");
|
||||||
|
json_document failed = json_document::parse("not json", /* allow_exceptions */ false);
|
||||||
|
|
||||||
|
for (const json_view v :
|
||||||
|
{
|
||||||
|
good.root(), bad.root(), failed.root()
|
||||||
|
})
|
||||||
|
{
|
||||||
|
if (v.is_object())
|
||||||
|
{
|
||||||
|
std::cout << "ok\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << "expected an object, got " << v.type_name() << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
ok
|
||||||
|
expected an object, got array
|
||||||
|
expected an object, got discarded
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
using json_view = nlohmann::json_view;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
json_document doc = json_document::parse(R"({"server": {"host": "localhost"}})");
|
||||||
|
const json_view server = doc.root()["server"];
|
||||||
|
|
||||||
|
// "port" is missing -- value() returns the default instead of
|
||||||
|
// throwing, so optional configuration fields never need their own
|
||||||
|
// try/catch
|
||||||
|
std::cout << server.value("host", std::string("0.0.0.0")) << '\n';
|
||||||
|
std::cout << server.value("port", 8080) << '\n';
|
||||||
|
|
||||||
|
// a present but wrong-typed default still throws -- value() only
|
||||||
|
// replaces "not found", not "wrong type", exactly as basic_json::value
|
||||||
|
try
|
||||||
|
{
|
||||||
|
static_cast<void>(server.value("host", 0));
|
||||||
|
}
|
||||||
|
catch (const nlohmann::json::type_error& e)
|
||||||
|
{
|
||||||
|
std::cout << e.what() << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
localhost
|
||||||
|
8080
|
||||||
|
[json.exception.type_error.302] type must be number, but is string
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
using json_view = nlohmann::json_view;
|
||||||
|
using json_pointer = nlohmann::json::json_pointer;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
json_document doc = json_document::parse(R"({"server": {"host": "localhost", "limits": {"connections": 100}}})");
|
||||||
|
const json_view config = doc.root();
|
||||||
|
|
||||||
|
// a nested, optional setting read with a default -- no exception, even
|
||||||
|
// though "timeout" is missing several levels down
|
||||||
|
std::cout << config.value(json_pointer("/server/limits/connections"), 10) << '\n';
|
||||||
|
std::cout << config.value(json_pointer("/server/limits/timeout"), 30) << '\n';
|
||||||
|
|
||||||
|
// an out-of-range array index also falls back to the default
|
||||||
|
json_document list_doc = json_document::parse(R"({"servers": ["a", "b"]})");
|
||||||
|
std::cout << list_doc.root().value(json_pointer("/servers/5"), std::string("none")) << '\n';
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
100
|
||||||
|
30
|
||||||
|
none
|
||||||
@@ -22,10 +22,15 @@ actually needed (for a string, only if it contains escape sequences, into one sh
|
|||||||
[`basic_json_view`](../api/basic_json_view/index.md) is a small, trivially copyable handle (two pointers) into that
|
[`basic_json_view`](../api/basic_json_view/index.md) is a small, trivially copyable handle (two pointers) into that
|
||||||
index. It gives you the read-only, type-inspection part of the `basic_json` interface --
|
index. It gives you the read-only, type-inspection part of the `basic_json` interface --
|
||||||
[`type()`](../api/basic_json_view/type.md) and the `is_*()` predicates,
|
[`type()`](../api/basic_json_view/type.md) and the `is_*()` predicates,
|
||||||
[`size()`](../api/basic_json_view/size.md)/[`empty()`](../api/basic_json_view/empty.md) -- without ever allocating a
|
[`size()`](../api/basic_json_view/size.md)/[`empty()`](../api/basic_json_view/empty.md) -- as well as element access
|
||||||
`basic_json` value. When you do need an actual `basic_json` value for a subtree,
|
([`operator[]`](../api/basic_json_view/operator%5B%5D.md), [`at`](../api/basic_json_view/at.md),
|
||||||
[`materialize()`](../api/basic_json_view/materialize.md) builds exactly the one
|
[`front`](../api/basic_json_view/front.md)/[`back`](../api/basic_json_view/back.md)), lookup
|
||||||
[`parse()`](../api/basic_json/parse.md) would have produced for it.
|
([`find`](../api/basic_json_view/find.md), [`contains`](../api/basic_json_view/contains.md),
|
||||||
|
[`count`](../api/basic_json_view/count.md)), and iteration
|
||||||
|
([`begin`](../api/basic_json_view/begin.md)/[`end`](../api/basic_json_view/end.md),
|
||||||
|
[`items`](../api/basic_json_view/items.md)) -- without ever allocating a `basic_json` value. When you do need an
|
||||||
|
actual `basic_json` value for a subtree, [`materialize()`](../api/basic_json_view/materialize.md) builds exactly the
|
||||||
|
one [`parse()`](../api/basic_json/parse.md) would have produced for it.
|
||||||
|
|
||||||
## How to use it
|
## How to use it
|
||||||
|
|
||||||
@@ -116,9 +121,50 @@ whenever any of the other conditions above was not met.
|
|||||||
[`JSON_DIAGNOSTIC_POSITIONS`](../api/macros/json_diagnostic_positions.md) enabled,
|
[`JSON_DIAGNOSTIC_POSITIONS`](../api/macros/json_diagnostic_positions.md) enabled,
|
||||||
[`materialize()`](../api/basic_json_view/materialize.md) does not set them: there is no lexer run during the
|
[`materialize()`](../api/basic_json_view/materialize.md) does not set them: there is no lexer run during the
|
||||||
replay to record them.
|
replay to record them.
|
||||||
- **Element access, iteration, `get<T>()`, JSON Pointer, `dump()`, and comparison are not (yet) provided** by
|
- **Objects iterate in document order.** [`begin()`](../api/basic_json_view/begin.md)/
|
||||||
`basic_json_view`. For now, [`materialize()`](../api/basic_json_view/materialize.md) is the way to get a value you
|
[`end()`](../api/basic_json_view/end.md) and [`items()`](../api/basic_json_view/items.md) visit an object's
|
||||||
can do those things with.
|
members in the order they appear in the source text. `basic_json`'s default `object_t` is a `std::map`, which
|
||||||
|
sorts by key, so iterating a [`materialize()`](../api/basic_json_view/materialize.md)d value can print members in
|
||||||
|
a different order than iterating the view they came from.
|
||||||
|
- **Duplicate keys are visible.** If an object in the source text repeats a key,
|
||||||
|
[`begin()`](../api/basic_json_view/begin.md)/[`end()`](../api/basic_json_view/end.md) and
|
||||||
|
[`items()`](../api/basic_json_view/items.md) visit *every* occurrence (and [`size()`](../api/basic_json_view/size.md)
|
||||||
|
counts all of them), while [`operator[]`](../api/basic_json_view/operator%5B%5D.md),
|
||||||
|
[`at`](../api/basic_json_view/at.md), [`find`](../api/basic_json_view/find.md),
|
||||||
|
[`contains`](../api/basic_json_view/contains.md), and [`count`](../api/basic_json_view/count.md) resolve to the
|
||||||
|
*first* occurrence, since a lookup can stop as soon as it finds a match. `basic_json::parse()` (and so
|
||||||
|
[`materialize()`](../api/basic_json_view/materialize.md)) instead keeps only the *last* value for a repeated key.
|
||||||
|
See the [Notes on duplicate keys](../api/basic_json_view/operator%5B%5D.md#notes) of `operator[]`.
|
||||||
|
- **No [`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md) path.** Exceptions thrown by `basic_json_view`'s own
|
||||||
|
element access and lookup functions never carry the JSON Pointer path `JSON_DIAGNOSTICS` would otherwise add: the
|
||||||
|
view has no `basic_json` value to point at, so the exception is created without one, regardless of how
|
||||||
|
`BasicJsonType` was built.
|
||||||
|
- **`dump()` and comparison are not (yet) provided** by `basic_json_view`. For now,
|
||||||
|
[`materialize()`](../api/basic_json_view/materialize.md) is the way to get a value you can do those things with.
|
||||||
|
|
||||||
|
## Getting values out without copying
|
||||||
|
|
||||||
|
[`get<T>()`](../api/basic_json_view/get.md) converts many `T` directly from the flat index, without ever building a
|
||||||
|
`basic_json` value for the conversion: `#!cpp bool`, arithmetic types, `#!cpp std::nullptr_t`,
|
||||||
|
`#!cpp std::string`/other `#!cpp std::basic_string`s (copied once), `basic_json`/`ordered_json` (via
|
||||||
|
[`materialize()`](../api/basic_json_view/materialize.md)), `basic_json_view` itself, `#!cpp std::vector<U>`, and
|
||||||
|
`#!cpp std::map`/`#!cpp std::unordered_map` with string-like keys. Every other type -- `#!cpp std::list`,
|
||||||
|
`#!cpp std::pair`, `#!cpp std::array`, enumerations, user types with a `from_json()` -- goes through
|
||||||
|
[`materialize()`](../api/basic_json_view/materialize.md)`.get<T>()` instead: the subtree is built into a real
|
||||||
|
`basic_json` value first, exactly as [`parse()`](../api/basic_json/parse.md) would, and converted from there.
|
||||||
|
|
||||||
|
Two conversions never copy at all:
|
||||||
|
|
||||||
|
- [`get_string()`](../api/basic_json_view/get_string.md) (equivalently, `#!cpp get<string_view_t>()`) returns a
|
||||||
|
string as a `string_view_t` pointing into the document's [`source()`](../api/basic_json_document/source.md) text --
|
||||||
|
or, for a string that contains escape sequences, into the document's own buffer of decoded strings -- instead of
|
||||||
|
allocating a new `#!cpp std::string`.
|
||||||
|
- [`number_token()`](../api/basic_json_view/number_token.md) returns a number exactly as it was written in the
|
||||||
|
source, e.g. `#!cpp "1.50"`, `#!cpp "1E2"`, or an integer with more digits than any number type holds, instead of
|
||||||
|
rounding it into a `#!cpp double`/`#!cpp int64_t` the way `#!cpp get<T>()` (and
|
||||||
|
[`basic_json::parse()`](../api/basic_json/parse.md)) would.
|
||||||
|
|
||||||
|
Both results are only valid as long as the view -- and, for a string with no escapes, the borrowed source text -- is.
|
||||||
|
|
||||||
## Choosing between `json`, `ordered_json`, the SAX interface, and `json_view`
|
## Choosing between `json`, `ordered_json`, the SAX interface, and `json_view`
|
||||||
|
|
||||||
|
|||||||
@@ -246,7 +246,20 @@ nav:
|
|||||||
- basic_json_view:
|
- basic_json_view:
|
||||||
- 'Overview': api/basic_json_view/index.md
|
- 'Overview': api/basic_json_view/index.md
|
||||||
- '(Constructor)': api/basic_json_view/basic_json_view.md
|
- '(Constructor)': api/basic_json_view/basic_json_view.md
|
||||||
|
- 'at': api/basic_json_view/at.md
|
||||||
|
- 'back': api/basic_json_view/back.md
|
||||||
|
- 'begin': api/basic_json_view/begin.md
|
||||||
|
- 'cbegin': api/basic_json_view/cbegin.md
|
||||||
|
- 'cend': api/basic_json_view/cend.md
|
||||||
|
- 'contains': api/basic_json_view/contains.md
|
||||||
|
- 'count': api/basic_json_view/count.md
|
||||||
- 'empty': api/basic_json_view/empty.md
|
- 'empty': api/basic_json_view/empty.md
|
||||||
|
- 'end': api/basic_json_view/end.md
|
||||||
|
- 'find': api/basic_json_view/find.md
|
||||||
|
- 'front': api/basic_json_view/front.md
|
||||||
|
- 'get': api/basic_json_view/get.md
|
||||||
|
- 'get_string': api/basic_json_view/get_string.md
|
||||||
|
- 'get_to': api/basic_json_view/get_to.md
|
||||||
- 'is_array': api/basic_json_view/is_array.md
|
- 'is_array': api/basic_json_view/is_array.md
|
||||||
- 'is_binary': api/basic_json_view/is_binary.md
|
- 'is_binary': api/basic_json_view/is_binary.md
|
||||||
- 'is_boolean': api/basic_json_view/is_boolean.md
|
- 'is_boolean': api/basic_json_view/is_boolean.md
|
||||||
@@ -260,11 +273,16 @@ nav:
|
|||||||
- 'is_primitive': api/basic_json_view/is_primitive.md
|
- 'is_primitive': api/basic_json_view/is_primitive.md
|
||||||
- 'is_string': api/basic_json_view/is_string.md
|
- 'is_string': api/basic_json_view/is_string.md
|
||||||
- 'is_structured': api/basic_json_view/is_structured.md
|
- 'is_structured': api/basic_json_view/is_structured.md
|
||||||
|
- 'items': api/basic_json_view/items.md
|
||||||
- 'materialize': api/basic_json_view/materialize.md
|
- 'materialize': api/basic_json_view/materialize.md
|
||||||
|
- 'number_token': api/basic_json_view/number_token.md
|
||||||
- 'operator bool': api/basic_json_view/operator_bool.md
|
- 'operator bool': api/basic_json_view/operator_bool.md
|
||||||
|
- 'operator[]': api/basic_json_view/operator[].md
|
||||||
- 'size': api/basic_json_view/size.md
|
- 'size': api/basic_json_view/size.md
|
||||||
- 'source_offset': api/basic_json_view/source_offset.md
|
- 'source_offset': api/basic_json_view/source_offset.md
|
||||||
- 'type': api/basic_json_view/type.md
|
- 'type': api/basic_json_view/type.md
|
||||||
|
- 'type_name': api/basic_json_view/type_name.md
|
||||||
|
- 'value': api/basic_json_view/value.md
|
||||||
- byte_container_with_subtype:
|
- byte_container_with_subtype:
|
||||||
- 'Overview': api/byte_container_with_subtype/index.md
|
- 'Overview': api/byte_container_with_subtype/index.md
|
||||||
- '(constructor)': api/byte_container_with_subtype/byte_container_with_subtype.md
|
- '(constructor)': api/byte_container_with_subtype/byte_container_with_subtype.md
|
||||||
|
|||||||
@@ -31,6 +31,11 @@
|
|||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
struct json_pointer_access;
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/
|
/// @sa https://json.nlohmann.me/api/json_pointer/
|
||||||
template<typename RefStringType>
|
template<typename RefStringType>
|
||||||
@@ -43,6 +48,8 @@ class json_pointer
|
|||||||
template<typename>
|
template<typename>
|
||||||
friend class json_pointer;
|
friend class json_pointer;
|
||||||
|
|
||||||
|
friend struct detail::json_pointer_access;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct string_t_helper
|
struct string_t_helper
|
||||||
{
|
{
|
||||||
@@ -1171,4 +1178,18 @@ inline bool operator<(const json_pointer<RefStringTypeLhs>& lhs,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
/// the reference tokens of a json_pointer, for code that resolves pointers
|
||||||
|
/// without a basic_json value (such as the zero-copy view)
|
||||||
|
struct json_pointer_access
|
||||||
|
{
|
||||||
|
template<typename RefStringType>
|
||||||
|
static const std::vector<typename json_pointer<RefStringType>::string_t>& reference_tokens(const json_pointer<RefStringType>& ptr) noexcept
|
||||||
|
{
|
||||||
|
return ptr.reference_tokens;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|||||||
@@ -67,6 +67,39 @@ enum class value_t : std::uint8_t
|
|||||||
discarded ///< discarded by the parser callback function
|
discarded ///< discarded by the parser callback function
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief the name of a JSON type, as returned by basic_json::type_name()
|
||||||
|
|
||||||
|
Used in exception messages; also by code that reports types without a
|
||||||
|
basic_json value at hand (such as the zero-copy view).
|
||||||
|
*/
|
||||||
|
inline const char* value_type_name(const value_t t) noexcept
|
||||||
|
{
|
||||||
|
switch (t)
|
||||||
|
{
|
||||||
|
case value_t::null:
|
||||||
|
return "null";
|
||||||
|
case value_t::object:
|
||||||
|
return "object";
|
||||||
|
case value_t::array:
|
||||||
|
return "array";
|
||||||
|
case value_t::string:
|
||||||
|
return "string";
|
||||||
|
case value_t::boolean:
|
||||||
|
return "boolean";
|
||||||
|
case value_t::binary:
|
||||||
|
return "binary";
|
||||||
|
case value_t::discarded:
|
||||||
|
return "discarded";
|
||||||
|
case value_t::number_integer:
|
||||||
|
case value_t::number_unsigned:
|
||||||
|
case value_t::number_float:
|
||||||
|
return "number";
|
||||||
|
default:
|
||||||
|
return "invalid";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief comparison operator for JSON types
|
@brief comparison operator for JSON types
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,12 @@ namespace view
|
|||||||
NLOHMANN_VIEW_THROW(invalid_iterator::create(id, msg, nullptr));
|
NLOHMANN_VIEW_THROW(invalid_iterator::create(id, msg, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// a parse error without a position (as those of json_pointer)
|
||||||
|
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_parse_error(int id, const std::string& msg)
|
||||||
|
{
|
||||||
|
NLOHMANN_VIEW_THROW(parse_error::create(id, 0, msg, nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief throw the exception BasicJsonType::parse would throw for this input
|
@brief throw the exception BasicJsonType::parse would throw for this input
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,263 @@
|
|||||||
|
// __ _____ _____ _____
|
||||||
|
// __| | __| | | | JSON for Modern C++
|
||||||
|
// | | |__ | | | | | | version 3.12.0
|
||||||
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef> // ptrdiff_t, size_t
|
||||||
|
#include <iterator> // forward_iterator_tag
|
||||||
|
#include <string> // string, to_string
|
||||||
|
#include <type_traits> // enable_if
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <nlohmann/detail/view/document_data.hpp>
|
||||||
|
#include <nlohmann/detail/view/errors.hpp>
|
||||||
|
#include <nlohmann/detail/view/macro_scope.hpp>
|
||||||
|
#include <nlohmann/detail/view/node.hpp>
|
||||||
|
|
||||||
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
namespace view
|
||||||
|
{
|
||||||
|
|
||||||
|
/// the result of view_iterator::operator->: keeps the view alive for the
|
||||||
|
/// duration of the member access
|
||||||
|
template<typename View>
|
||||||
|
class arrow_proxy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit arrow_proxy(const View& v) noexcept
|
||||||
|
: m_view(v)
|
||||||
|
{}
|
||||||
|
|
||||||
|
const View* operator->() const noexcept
|
||||||
|
{
|
||||||
|
return &m_view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
View m_view;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief forward iterator over the elements of a basic_json_view
|
||||||
|
|
||||||
|
Iterates over the elements of an array or the member values of an object, in
|
||||||
|
document order; key() gives the key of an object member. As for basic_json, a
|
||||||
|
primitive value iterates as a range of one element (itself), and null as an
|
||||||
|
empty range.
|
||||||
|
*/
|
||||||
|
template<typename View>
|
||||||
|
class view_iterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using iterator_category = std::forward_iterator_tag;
|
||||||
|
using value_type = View;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using pointer = arrow_proxy<View>;
|
||||||
|
using reference = View;
|
||||||
|
using string_view_t = typename View::string_view_t;
|
||||||
|
|
||||||
|
view_iterator() noexcept = default;
|
||||||
|
|
||||||
|
/// @param[in] pos the element, or the key of the member
|
||||||
|
/// @param[in] object whether pos is a key (its value is the next node)
|
||||||
|
view_iterator(const document_data* d, const node* pos, bool object) noexcept
|
||||||
|
: m_doc(d), m_pos(pos), m_value_offset(object ? 1 : 0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
NLOHMANN_VIEW_ALWAYS_INLINE View operator*() const noexcept
|
||||||
|
{
|
||||||
|
return View(m_doc, m_pos + m_value_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer operator->() const noexcept
|
||||||
|
{
|
||||||
|
return pointer(**this);
|
||||||
|
}
|
||||||
|
|
||||||
|
NLOHMANN_VIEW_ALWAYS_INLINE view_iterator& operator++() noexcept
|
||||||
|
{
|
||||||
|
m_pos = document_data::after(m_pos + m_value_offset);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
view_iterator operator++(int) noexcept
|
||||||
|
{
|
||||||
|
const view_iterator r = *this;
|
||||||
|
++*this;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator==(const view_iterator& a, const view_iterator& b) noexcept
|
||||||
|
{
|
||||||
|
return a.m_pos == b.m_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const view_iterator& a, const view_iterator& b) noexcept
|
||||||
|
{
|
||||||
|
return a.m_pos != b.m_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the key of the current object member; throws invalid_iterator.207 for
|
||||||
|
/// other iterators, like basic_json's iterators
|
||||||
|
string_view_t key() const
|
||||||
|
{
|
||||||
|
if (NLOHMANN_VIEW_UNLIKELY(m_value_offset == 0))
|
||||||
|
{
|
||||||
|
throw_invalid_iterator(207, "cannot use key() for non-object iterators");
|
||||||
|
}
|
||||||
|
return string_view_t(m_doc->str(*m_pos), m_pos->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
View value() const noexcept
|
||||||
|
{
|
||||||
|
return **this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// whether the iterator runs over the members of an object
|
||||||
|
bool is_object_iterator() const noexcept
|
||||||
|
{
|
||||||
|
return m_value_offset != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const document_data* m_doc = nullptr;
|
||||||
|
const node* m_pos = nullptr;
|
||||||
|
std::size_t m_value_offset = 0; ///< 1 for objects: the value follows its key
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief a (key, value) item of basic_json_view::items()
|
||||||
|
|
||||||
|
The key of an array element is its index, as for basic_json::items().
|
||||||
|
Supports structured bindings: for (const auto [key, value] : view.items())
|
||||||
|
*/
|
||||||
|
template<typename View>
|
||||||
|
class view_item
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using string_view_t = typename View::string_view_t;
|
||||||
|
using iterator = view_iterator<View>;
|
||||||
|
|
||||||
|
view_item(const iterator& it, std::size_t index)
|
||||||
|
: m_it(it)
|
||||||
|
{
|
||||||
|
if (!it.is_object_iterator())
|
||||||
|
{
|
||||||
|
m_index = std::to_string(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the member key, or the element index for arrays
|
||||||
|
string_view_t key() const
|
||||||
|
{
|
||||||
|
if (m_it.is_object_iterator())
|
||||||
|
{
|
||||||
|
return m_it.key();
|
||||||
|
}
|
||||||
|
return string_view_t(m_index.data(), m_index.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
View value() const noexcept
|
||||||
|
{
|
||||||
|
return *m_it;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::size_t N, typename std::enable_if<N == 0, int>::type = 0>
|
||||||
|
string_view_t get() const
|
||||||
|
{
|
||||||
|
return key();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::size_t N, typename std::enable_if<N == 1, int>::type = 0>
|
||||||
|
View get() const noexcept
|
||||||
|
{
|
||||||
|
return value();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
iterator m_it;
|
||||||
|
std::string m_index{}; // NOLINT(readability-redundant-member-init)
|
||||||
|
};
|
||||||
|
|
||||||
|
/// the range returned by basic_json_view::items()
|
||||||
|
template<typename View>
|
||||||
|
class view_items
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using item = view_item<View>;
|
||||||
|
|
||||||
|
class iterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using iterator_category = std::forward_iterator_tag;
|
||||||
|
using value_type = item;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using pointer = void;
|
||||||
|
using reference = item;
|
||||||
|
|
||||||
|
explicit iterator(const view_iterator<View>& it) noexcept
|
||||||
|
: m_it(it)
|
||||||
|
{}
|
||||||
|
|
||||||
|
item operator*() const
|
||||||
|
{
|
||||||
|
return item(m_it, m_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator& operator++() noexcept
|
||||||
|
{
|
||||||
|
++m_it;
|
||||||
|
++m_index;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator operator++(int) noexcept
|
||||||
|
{
|
||||||
|
const iterator r = *this;
|
||||||
|
++*this;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator==(const iterator& a, const iterator& b) noexcept
|
||||||
|
{
|
||||||
|
return a.m_it == b.m_it;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const iterator& a, const iterator& b) noexcept
|
||||||
|
{
|
||||||
|
return a.m_it != b.m_it;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
view_iterator<View> m_it;
|
||||||
|
std::size_t m_index = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit view_items(const View& v) noexcept
|
||||||
|
: m_view(v)
|
||||||
|
{}
|
||||||
|
|
||||||
|
iterator begin() const noexcept
|
||||||
|
{
|
||||||
|
return iterator(m_view.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator end() const noexcept
|
||||||
|
{
|
||||||
|
return iterator(m_view.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
View m_view;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace view
|
||||||
|
} // namespace detail
|
||||||
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
// __ _____ _____ _____
|
||||||
|
// __| | __| | | | JSON for Modern C++
|
||||||
|
// | | |__ | | | | | | version 3.12.0
|
||||||
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef> // size_t
|
||||||
|
#include <cstdint> // uint16_t, uint32_t, uint64_t
|
||||||
|
#include <cstring> // memcmp, memcpy
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <nlohmann/detail/view/document_data.hpp>
|
||||||
|
#include <nlohmann/detail/view/macro_scope.hpp>
|
||||||
|
#include <nlohmann/detail/view/node.hpp>
|
||||||
|
|
||||||
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
namespace view
|
||||||
|
{
|
||||||
|
|
||||||
|
/// equality test for strings of one length n <= 16: two overlapping loads per
|
||||||
|
/// string (the first and the last 8, 4, or 2 bytes) replace a memcmp, and no
|
||||||
|
/// byte outside [s, s + n) is read
|
||||||
|
class short_key
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
short_key(const unsigned char* k, std::size_t n) noexcept
|
||||||
|
: m_n(n)
|
||||||
|
{
|
||||||
|
load(k, m_a, m_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
NLOHMANN_VIEW_ALWAYS_INLINE bool matches(const unsigned char* s) const noexcept
|
||||||
|
{
|
||||||
|
std::uint64_t a = 0;
|
||||||
|
std::uint64_t b = 0;
|
||||||
|
load(s, a, b);
|
||||||
|
return a == m_a && b == m_b;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<typename T>
|
||||||
|
static NLOHMANN_VIEW_ALWAYS_INLINE std::uint64_t load_word(const unsigned char* s) noexcept
|
||||||
|
{
|
||||||
|
T w = 0;
|
||||||
|
std::memcpy(&w, s, sizeof(T));
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
NLOHMANN_VIEW_ALWAYS_INLINE void load(const unsigned char* s, std::uint64_t& a, std::uint64_t& b) const noexcept
|
||||||
|
{
|
||||||
|
if (m_n >= 8)
|
||||||
|
{
|
||||||
|
a = load_word<std::uint64_t>(s);
|
||||||
|
b = load_word<std::uint64_t>(s + m_n - 8);
|
||||||
|
}
|
||||||
|
else if (m_n >= 4)
|
||||||
|
{
|
||||||
|
a = load_word<std::uint32_t>(s);
|
||||||
|
b = load_word<std::uint32_t>(s + m_n - 4);
|
||||||
|
}
|
||||||
|
else if (m_n >= 2)
|
||||||
|
{
|
||||||
|
a = load_word<std::uint16_t>(s);
|
||||||
|
b = load_word<std::uint16_t>(s + m_n - 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
a = m_n == 1 ? s[0] : 0;
|
||||||
|
b = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t m_n;
|
||||||
|
std::uint64_t m_a = 0;
|
||||||
|
std::uint64_t m_b = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// the key node of the first member of an object with the given key, or
|
||||||
|
/// nullptr; most keys are rejected by their length, from the index alone
|
||||||
|
inline const node* find_member(const document_data& d, const node* object, const char* key, std::size_t n) noexcept
|
||||||
|
{
|
||||||
|
const node* const end = document_data::child_end(object);
|
||||||
|
const auto* const k = reinterpret_cast<const unsigned char*>(key); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||||
|
if (NLOHMANN_VIEW_LIKELY(n <= 16))
|
||||||
|
{
|
||||||
|
const short_key probe(k, n);
|
||||||
|
for (const node* m = document_data::first_child(object); m != end; m = document_data::after(m + 1))
|
||||||
|
{
|
||||||
|
if (m->len == n && probe.matches(reinterpret_cast<const unsigned char*>(d.str(*m)))) // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||||
|
{
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
for (const node* m = document_data::first_child(object); m != end; m = document_data::after(m + 1))
|
||||||
|
{
|
||||||
|
if (m->len == n && std::memcmp(d.str(*m), key, n) == 0)
|
||||||
|
{
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the element of an array at an index below its size
|
||||||
|
inline const node* element_at(const node* array, std::size_t idx) noexcept
|
||||||
|
{
|
||||||
|
const node* e = document_data::first_child(array);
|
||||||
|
for (std::size_t i = 0; i < idx; ++i)
|
||||||
|
{
|
||||||
|
e = document_data::after(e);
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the last element of a non-empty array, or the key of the last member of a
|
||||||
|
/// non-empty object
|
||||||
|
inline const node* last_child(const node* container) noexcept
|
||||||
|
{
|
||||||
|
const std::size_t value_offset = container->kind == static_cast<std::uint8_t>(value_t::object) ? 1 : 0;
|
||||||
|
const node* const end = document_data::child_end(container);
|
||||||
|
const node* last = document_data::first_child(container);
|
||||||
|
for (const node* c = document_data::after(last + value_offset); c != end; c = document_data::after(c + value_offset))
|
||||||
|
{
|
||||||
|
last = c;
|
||||||
|
}
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace view
|
||||||
|
} // namespace detail
|
||||||
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
// __ _____ _____ _____
|
||||||
|
// __| | __| | | | JSON for Modern C++
|
||||||
|
// | | |__ | | | | | | version 3.12.0
|
||||||
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef> // size_t
|
||||||
|
#include <cstdint> // uint64_t
|
||||||
|
#include <limits> // numeric_limits
|
||||||
|
#include <string> // string, to_string
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <nlohmann/detail/view/errors.hpp>
|
||||||
|
#include <nlohmann/detail/view/macro_scope.hpp>
|
||||||
|
|
||||||
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
namespace view
|
||||||
|
{
|
||||||
|
|
||||||
|
/// what resolving a JSON pointer does where it cannot continue
|
||||||
|
enum class pointer_mode
|
||||||
|
{
|
||||||
|
unchecked, ///< as const basic_json::operator[]: a discarded view where basic_json's behavior is undefined
|
||||||
|
checked, ///< as basic_json::at(): out_of_range.401/403
|
||||||
|
value, ///< as basic_json::value(): no out_of_range exceptions (the default value is used)
|
||||||
|
contains, ///< as basic_json::contains(): no exceptions at all
|
||||||
|
};
|
||||||
|
|
||||||
|
/// the outcome of reading an array index from a reference token
|
||||||
|
enum class index_status
|
||||||
|
{
|
||||||
|
ok,
|
||||||
|
leading_zero, ///< parse_error.106
|
||||||
|
not_number, ///< parse_error.109
|
||||||
|
unresolved, ///< out_of_range.404
|
||||||
|
too_large, ///< out_of_range.410
|
||||||
|
};
|
||||||
|
|
||||||
|
/// reads an array index like json_pointer::array_index (RFC 6901, Sect. 4),
|
||||||
|
/// but reports errors instead of throwing them
|
||||||
|
template<typename StringType>
|
||||||
|
index_status array_index(const StringType& s, std::size_t& idx) noexcept
|
||||||
|
{
|
||||||
|
if (s.size() > 1 && s[0] == '0')
|
||||||
|
{
|
||||||
|
return index_status::leading_zero;
|
||||||
|
}
|
||||||
|
if (s.size() > 1 && !(s[0] >= '1' && s[0] <= '9'))
|
||||||
|
{
|
||||||
|
return index_status::not_number;
|
||||||
|
}
|
||||||
|
if (s.empty())
|
||||||
|
{
|
||||||
|
return index_status::unresolved;
|
||||||
|
}
|
||||||
|
std::uint64_t v = 0;
|
||||||
|
for (std::size_t i = 0; i < s.size(); ++i)
|
||||||
|
{
|
||||||
|
const auto d = static_cast<unsigned>(static_cast<unsigned char>(s[i])) - '0';
|
||||||
|
if (d > 9 || v > ((std::numeric_limits<std::uint64_t>::max)() - d) / 10)
|
||||||
|
{
|
||||||
|
return index_status::unresolved; // not a number, or beyond unsigned long long
|
||||||
|
}
|
||||||
|
v = (v * 10) + d;
|
||||||
|
}
|
||||||
|
if (v >= (std::numeric_limits<std::size_t>::max)()) // (std::size_t converts to std::uint64_t implicitly)
|
||||||
|
{
|
||||||
|
return index_status::too_large;
|
||||||
|
}
|
||||||
|
idx = static_cast<std::size_t>(v);
|
||||||
|
return index_status::ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// throws the exception json_pointer::array_index throws for this status
|
||||||
|
template<typename StringType>
|
||||||
|
[[noreturn]] NLOHMANN_VIEW_NOINLINE void throw_array_index_error(index_status status, const StringType& s)
|
||||||
|
{
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case index_status::leading_zero:
|
||||||
|
throw_parse_error(106, concat("array index '", s, "' must not begin with '0'"));
|
||||||
|
case index_status::not_number:
|
||||||
|
throw_parse_error(109, concat("array index '", s, "' is not a number"));
|
||||||
|
case index_status::too_large:
|
||||||
|
throw_out_of_range(410, concat("array index ", s, " exceeds size_type")); // LCOV_EXCL_LINE
|
||||||
|
case index_status::unresolved:
|
||||||
|
case index_status::ok:
|
||||||
|
default:
|
||||||
|
throw_out_of_range(404, concat("unresolved reference token '", s, "'"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief resolve the reference tokens of a JSON pointer, starting at a view
|
||||||
|
|
||||||
|
The exceptions are those basic_json throws for the same pointer; where
|
||||||
|
basic_json's behavior is undefined (a missing key or an index out of range
|
||||||
|
with const operator[]), the result is a discarded view.
|
||||||
|
*/
|
||||||
|
template<typename View, typename Tokens>
|
||||||
|
View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode)
|
||||||
|
{
|
||||||
|
using string_view_t = typename View::string_view_t;
|
||||||
|
const bool throwing = mode == pointer_mode::unchecked || mode == pointer_mode::checked;
|
||||||
|
for (const auto& token : tokens)
|
||||||
|
{
|
||||||
|
if (cur.is_object())
|
||||||
|
{
|
||||||
|
const auto it = cur.find(string_view_t(token.data(), token.size()));
|
||||||
|
if (it == cur.end())
|
||||||
|
{
|
||||||
|
if (mode == pointer_mode::checked)
|
||||||
|
{
|
||||||
|
throw_out_of_range(403, concat("key '", token, "' not found"));
|
||||||
|
}
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
cur = *it;
|
||||||
|
}
|
||||||
|
else if (cur.is_array())
|
||||||
|
{
|
||||||
|
if (token.size() == 1 && token[0] == '-')
|
||||||
|
{
|
||||||
|
if (throwing)
|
||||||
|
{
|
||||||
|
throw_out_of_range(402, concat("array index '-' (", std::to_string(cur.size()), ") is out of range"));
|
||||||
|
}
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
std::size_t idx = 0;
|
||||||
|
const index_status status = array_index(token, idx);
|
||||||
|
if (status != index_status::ok)
|
||||||
|
{
|
||||||
|
const bool parse_error = status == index_status::leading_zero || status == index_status::not_number;
|
||||||
|
if (throwing || (mode == pointer_mode::value && parse_error))
|
||||||
|
{
|
||||||
|
throw_array_index_error(status, token);
|
||||||
|
}
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
if (idx >= cur.size())
|
||||||
|
{
|
||||||
|
if (mode == pointer_mode::checked)
|
||||||
|
{
|
||||||
|
throw_out_of_range(401, concat("array index ", std::to_string(idx), " is out of range"));
|
||||||
|
}
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
cur = cur[idx];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (throwing)
|
||||||
|
{
|
||||||
|
throw_out_of_range(404, concat("unresolved reference token '", token, "'"));
|
||||||
|
}
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace view
|
||||||
|
} // namespace detail
|
||||||
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
// __ _____ _____ _____
|
||||||
|
// __| | __| | | | JSON for Modern C++
|
||||||
|
// | | |__ | | | | | | version 3.12.0
|
||||||
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint> // int64_t
|
||||||
|
#include <map> // map
|
||||||
|
#include <string> // basic_string
|
||||||
|
#include <type_traits> // enable_if, is_constructible
|
||||||
|
#include <unordered_map> // unordered_map
|
||||||
|
#include <vector> // vector
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <nlohmann/detail/view/document_data.hpp>
|
||||||
|
#include <nlohmann/detail/view/errors.hpp>
|
||||||
|
#include <nlohmann/detail/view/macro_scope.hpp>
|
||||||
|
#include <nlohmann/detail/view/node.hpp>
|
||||||
|
#include <nlohmann/detail/view/number.hpp>
|
||||||
|
|
||||||
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
namespace view
|
||||||
|
{
|
||||||
|
|
||||||
|
/// selects a conversion by its target type
|
||||||
|
template<typename T>
|
||||||
|
struct value_tag {};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief the number or boolean of a node converted to an arithmetic type
|
||||||
|
|
||||||
|
As basic_json's get<T>() for arithmetic types: integers and floats are
|
||||||
|
converted with static_cast, booleans give 0 or 1, and other types throw
|
||||||
|
type_error.302.
|
||||||
|
*/
|
||||||
|
template<typename T, typename BasicJsonType>
|
||||||
|
NLOHMANN_VIEW_ALWAYS_INLINE T arithmetic_value(const document_data& d, const node& n)
|
||||||
|
{
|
||||||
|
switch (static_cast<value_t>(n.kind))
|
||||||
|
{
|
||||||
|
case value_t::number_unsigned:
|
||||||
|
return static_cast<T>(static_cast<typename BasicJsonType::number_unsigned_t>(integer_bits(n)));
|
||||||
|
case value_t::number_integer:
|
||||||
|
return static_cast<T>(static_cast<typename BasicJsonType::number_integer_t>(static_cast<std::int64_t>(integer_bits(n))));
|
||||||
|
case value_t::number_float:
|
||||||
|
return static_cast<T>(float_value<typename BasicJsonType::number_float_t>(d.str(n), n));
|
||||||
|
case value_t::boolean:
|
||||||
|
return static_cast<T>((n.flags & node_flags::is_true) != 0);
|
||||||
|
case value_t::null:
|
||||||
|
case value_t::object:
|
||||||
|
case value_t::array:
|
||||||
|
case value_t::string:
|
||||||
|
case value_t::binary:
|
||||||
|
case value_t::discarded:
|
||||||
|
default:
|
||||||
|
throw_type_error(302, "type must be number, but is ", value_type_name(static_cast<value_t>(n.kind)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// std::vector from an array, element by element (type_error.302 otherwise)
|
||||||
|
template<typename View, typename U, typename A>
|
||||||
|
std::vector<U, A> vector_value(const View& v)
|
||||||
|
{
|
||||||
|
if (NLOHMANN_VIEW_UNLIKELY(!v.is_array()))
|
||||||
|
{
|
||||||
|
throw_type_error(302, "type must be array, but is ", v.type_name());
|
||||||
|
}
|
||||||
|
std::vector<U, A> r;
|
||||||
|
r.reserve(v.size());
|
||||||
|
for (const View e : v)
|
||||||
|
{
|
||||||
|
r.push_back(e.template get<U>());
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// a map with string keys from an object; with duplicate keys, the last
|
||||||
|
/// value is kept, as parse() does (type_error.302 for other types)
|
||||||
|
template<typename Map, typename View>
|
||||||
|
Map map_value(const View& v)
|
||||||
|
{
|
||||||
|
if (NLOHMANN_VIEW_UNLIKELY(!v.is_object()))
|
||||||
|
{
|
||||||
|
throw_type_error(302, "type must be object, but is ", v.type_name());
|
||||||
|
}
|
||||||
|
Map r;
|
||||||
|
for (auto it = v.begin(); it != v.end(); ++it)
|
||||||
|
{
|
||||||
|
const auto key = it.key();
|
||||||
|
r[typename Map::key_type(key.data(), key.size())] = it.value().template get<typename Map::mapped_type>();
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// whether a map type is read member by member (its keys are made from
|
||||||
|
/// characters and a length); other maps go through basic_json
|
||||||
|
template<typename Key>
|
||||||
|
struct is_string_key : std::is_constructible<Key, const char*, std::size_t> {};
|
||||||
|
|
||||||
|
} // namespace view
|
||||||
|
} // namespace detail
|
||||||
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
@@ -5351,29 +5351,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
JSON_HEDLEY_RETURNS_NON_NULL
|
JSON_HEDLEY_RETURNS_NON_NULL
|
||||||
const char* type_name() const noexcept
|
const char* type_name() const noexcept
|
||||||
{
|
{
|
||||||
switch (m_data.m_type)
|
return detail::value_type_name(m_data.m_type);
|
||||||
{
|
|
||||||
case value_t::null:
|
|
||||||
return "null";
|
|
||||||
case value_t::object:
|
|
||||||
return "object";
|
|
||||||
case value_t::array:
|
|
||||||
return "array";
|
|
||||||
case value_t::string:
|
|
||||||
return "string";
|
|
||||||
case value_t::boolean:
|
|
||||||
return "boolean";
|
|
||||||
case value_t::binary:
|
|
||||||
return "binary";
|
|
||||||
case value_t::discarded:
|
|
||||||
return "discarded";
|
|
||||||
case value_t::number_integer:
|
|
||||||
case value_t::number_unsigned:
|
|
||||||
case value_t::number_float:
|
|
||||||
return "number";
|
|
||||||
default:
|
|
||||||
return "invalid";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_PRIVATE_UNLESS_TESTED:
|
JSON_PRIVATE_UNLESS_TESTED:
|
||||||
|
|||||||
@@ -27,10 +27,14 @@
|
|||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <cstring> // memcpy, strlen
|
#include <cstring> // memcpy, strlen
|
||||||
#include <iterator> // distance, input_iterator_tag, iterator_traits
|
#include <iterator> // distance, input_iterator_tag, iterator_traits
|
||||||
|
#include <map> // map
|
||||||
#include <memory> // unique_ptr
|
#include <memory> // unique_ptr
|
||||||
#include <string> // string
|
#include <string> // string
|
||||||
#include <type_traits> // enable_if, integral_constant, is_base_of, is_integral, is_same, remove_cv, remove_extent
|
#include <tuple> // tuple_element, tuple_size
|
||||||
|
#include <type_traits> // decay, enable_if, integral_constant, is_arithmetic, is_base_of, is_integral, is_same, remove_cv, remove_extent
|
||||||
|
#include <unordered_map> // unordered_map
|
||||||
#include <utility> // forward, move
|
#include <utility> // forward, move
|
||||||
|
#include <vector> // vector
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
@@ -43,10 +47,14 @@
|
|||||||
#include <nlohmann/detail/view/document_data.hpp>
|
#include <nlohmann/detail/view/document_data.hpp>
|
||||||
#include <nlohmann/detail/view/errors.hpp>
|
#include <nlohmann/detail/view/errors.hpp>
|
||||||
#include <nlohmann/detail/view/input.hpp>
|
#include <nlohmann/detail/view/input.hpp>
|
||||||
|
#include <nlohmann/detail/view/iterator.hpp>
|
||||||
|
#include <nlohmann/detail/view/lookup.hpp>
|
||||||
#include <nlohmann/detail/view/macro_scope.hpp>
|
#include <nlohmann/detail/view/macro_scope.hpp>
|
||||||
#include <nlohmann/detail/view/materialize.hpp>
|
#include <nlohmann/detail/view/materialize.hpp>
|
||||||
#include <nlohmann/detail/view/node.hpp>
|
#include <nlohmann/detail/view/node.hpp>
|
||||||
|
#include <nlohmann/detail/view/pointer.hpp>
|
||||||
#include <nlohmann/detail/view/string_ref.hpp>
|
#include <nlohmann/detail/view/string_ref.hpp>
|
||||||
|
#include <nlohmann/detail/view/value.hpp>
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
@@ -75,6 +83,11 @@ class basic_json_view
|
|||||||
using size_type = std::size_t;
|
using size_type = std::size_t;
|
||||||
/// std::string_view from C++17 on
|
/// std::string_view from C++17 on
|
||||||
using string_view_t = detail::view::string_ref;
|
using string_view_t = detail::view::string_ref;
|
||||||
|
/// forward iterator over elements (arrays) or member values (objects)
|
||||||
|
using iterator = detail::view::view_iterator<basic_json_view>;
|
||||||
|
using const_iterator = iterator;
|
||||||
|
/// a (key, value) item of items()
|
||||||
|
using item = detail::view::view_item<basic_json_view>;
|
||||||
|
|
||||||
/// an invalid view (type() == value_t::discarded)
|
/// an invalid view (type() == value_t::discarded)
|
||||||
basic_json_view() noexcept = default;
|
basic_json_view() noexcept = default;
|
||||||
@@ -162,6 +175,12 @@ class basic_json_view
|
|||||||
return m_node != nullptr;
|
return m_node != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the name of the type, as basic_json::type_name()
|
||||||
|
const char* type_name() const noexcept
|
||||||
|
{
|
||||||
|
return detail::value_type_name(type());
|
||||||
|
}
|
||||||
|
|
||||||
//////////////
|
//////////////
|
||||||
// capacity //
|
// capacity //
|
||||||
//////////////
|
//////////////
|
||||||
@@ -211,6 +230,323 @@ class basic_json_view
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
// element access //
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
/// the value of the member with this key (the first one, should the key
|
||||||
|
/// occur more than once); a discarded view if there is none. Throws
|
||||||
|
/// type_error.305 if this is not an object.
|
||||||
|
NLOHMANN_VIEW_ALWAYS_INLINE basic_json_view operator[](string_view_t key) const
|
||||||
|
{
|
||||||
|
if (NLOHMANN_VIEW_UNLIKELY(!is_object()))
|
||||||
|
{
|
||||||
|
detail::view::throw_type_error(305, "cannot use operator[] with a string argument with ", type_name());
|
||||||
|
}
|
||||||
|
return lookup(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_json_view operator[](const char* key) const
|
||||||
|
{
|
||||||
|
return operator[](string_view_t(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_json_view operator[](const string_t& key) const
|
||||||
|
{
|
||||||
|
return operator[](string_view_t(key.data(), key.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the element at this index; a discarded view if the index is out of
|
||||||
|
/// range. Throws type_error.305 if this is not an array.
|
||||||
|
basic_json_view operator[](size_type idx) const
|
||||||
|
{
|
||||||
|
if (NLOHMANN_VIEW_UNLIKELY(!is_array()))
|
||||||
|
{
|
||||||
|
detail::view::throw_type_error(305, "cannot use operator[] with a numeric argument with ", type_name());
|
||||||
|
}
|
||||||
|
return idx < m_node->len ? basic_json_view(m_doc, detail::view::element_at(m_node, idx)) : basic_json_view();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// (an int argument would be ambiguous between size_type and const char*)
|
||||||
|
basic_json_view operator[](int idx) const
|
||||||
|
{
|
||||||
|
return operator[](static_cast<size_type>(idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the value a JSON pointer refers to; a discarded view if a key is
|
||||||
|
/// missing or an index is out of range. Other errors throw what const
|
||||||
|
/// basic_json::operator[] throws.
|
||||||
|
basic_json_view operator[](const json_pointer& ptr) const
|
||||||
|
{
|
||||||
|
return detail::view::resolve_pointer(*this, detail::json_pointer_access::reference_tokens(ptr), detail::view::pointer_mode::unchecked);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the value of the member with this key (the first one, should the key
|
||||||
|
/// occur more than once). Throws type_error.304 if this is not an object,
|
||||||
|
/// and out_of_range.403 if there is no such member.
|
||||||
|
basic_json_view at(string_view_t key) const
|
||||||
|
{
|
||||||
|
if (NLOHMANN_VIEW_UNLIKELY(!is_object()))
|
||||||
|
{
|
||||||
|
detail::view::throw_type_error(304, "cannot use at() with ", type_name());
|
||||||
|
}
|
||||||
|
const basic_json_view r = lookup(key);
|
||||||
|
if (NLOHMANN_VIEW_UNLIKELY(!r))
|
||||||
|
{
|
||||||
|
detail::view::throw_out_of_range(403, detail::concat("key '", std::string(key.data(), key.size()), "' not found"));
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_json_view at(const char* key) const
|
||||||
|
{
|
||||||
|
return at(string_view_t(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_json_view at(const string_t& key) const
|
||||||
|
{
|
||||||
|
return at(string_view_t(key.data(), key.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the element at this index. Throws type_error.304 if this is not an
|
||||||
|
/// array, and out_of_range.401 if the index is out of range.
|
||||||
|
basic_json_view at(size_type idx) const
|
||||||
|
{
|
||||||
|
if (NLOHMANN_VIEW_UNLIKELY(!is_array()))
|
||||||
|
{
|
||||||
|
detail::view::throw_type_error(304, "cannot use at() with ", type_name());
|
||||||
|
}
|
||||||
|
if (NLOHMANN_VIEW_UNLIKELY(idx >= m_node->len))
|
||||||
|
{
|
||||||
|
detail::view::throw_out_of_range(401, detail::concat("array index ", std::to_string(idx), " is out of range"));
|
||||||
|
}
|
||||||
|
return basic_json_view(m_doc, detail::view::element_at(m_node, idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_json_view at(int idx) const
|
||||||
|
{
|
||||||
|
return at(static_cast<size_type>(idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the value a JSON pointer refers to; throws what basic_json::at()
|
||||||
|
/// throws if it cannot be resolved
|
||||||
|
basic_json_view at(const json_pointer& ptr) const
|
||||||
|
{
|
||||||
|
return detail::view::resolve_pointer(*this, detail::json_pointer_access::reference_tokens(ptr), detail::view::pointer_mode::checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the member with this key converted to T, or the default value if there
|
||||||
|
/// is no such member (the first one, should the key occur more than
|
||||||
|
/// once). Throws type_error.306 if this is not an object.
|
||||||
|
template < typename T, typename std::enable_if < !std::is_same<typename std::decay<T>::type, const char*>::value, int >::type = 0 >
|
||||||
|
T value(string_view_t key, const T& default_value) const
|
||||||
|
{
|
||||||
|
if (NLOHMANN_VIEW_UNLIKELY(!is_object()))
|
||||||
|
{
|
||||||
|
detail::view::throw_type_error(306, "cannot use value() with ", type_name());
|
||||||
|
}
|
||||||
|
const basic_json_view r = lookup(key);
|
||||||
|
return r ? r.template get<T>() : default_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_t value(string_view_t key, const char* default_value) const
|
||||||
|
{
|
||||||
|
return value(key, string_t(default_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the value a JSON pointer refers to converted to T, or the default
|
||||||
|
/// value if the pointer cannot be resolved. Throws type_error.306 if this
|
||||||
|
/// is neither an object nor an array.
|
||||||
|
template < typename T, typename std::enable_if < !std::is_same<typename std::decay<T>::type, const char*>::value, int >::type = 0 >
|
||||||
|
T value(const json_pointer& ptr, const T& default_value) const
|
||||||
|
{
|
||||||
|
if (NLOHMANN_VIEW_UNLIKELY(!is_structured()))
|
||||||
|
{
|
||||||
|
detail::view::throw_type_error(306, "cannot use value() with ", type_name());
|
||||||
|
}
|
||||||
|
const basic_json_view r = detail::view::resolve_pointer(*this, detail::json_pointer_access::reference_tokens(ptr), detail::view::pointer_mode::value);
|
||||||
|
return r ? r.template get<T>() : default_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_t value(const json_pointer& ptr, const char* default_value) const
|
||||||
|
{
|
||||||
|
return value(ptr, string_t(default_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the first element or member value; a primitive value itself. Throws
|
||||||
|
/// invalid_iterator.214 for null, discarded views, and empty containers.
|
||||||
|
basic_json_view front() const
|
||||||
|
{
|
||||||
|
const iterator it = begin();
|
||||||
|
if (NLOHMANN_VIEW_UNLIKELY(it == end()))
|
||||||
|
{
|
||||||
|
detail::view::throw_invalid_iterator(214, "cannot get value");
|
||||||
|
}
|
||||||
|
return *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the last element or member value (linear in the size); a primitive
|
||||||
|
/// value itself. Throws invalid_iterator.214 for null, discarded views,
|
||||||
|
/// and empty containers.
|
||||||
|
basic_json_view back() const
|
||||||
|
{
|
||||||
|
if (is_structured() && m_node->len != 0)
|
||||||
|
{
|
||||||
|
return basic_json_view(m_doc, detail::view::last_child(m_node) + (is_object() ? 1 : 0));
|
||||||
|
}
|
||||||
|
return front();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////
|
||||||
|
// lookup //
|
||||||
|
////////////
|
||||||
|
|
||||||
|
/// an iterator to the member with this key (the first one, should the
|
||||||
|
/// key occur more than once), or end(); end() also for non-objects
|
||||||
|
iterator find(string_view_t key) const
|
||||||
|
{
|
||||||
|
if (!is_object())
|
||||||
|
{
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
const node* const k = detail::view::find_member(*m_doc, m_node, key.data(), key.size());
|
||||||
|
return k != nullptr ? iterator(m_doc, k, true) : end();
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator find(const char* key) const
|
||||||
|
{
|
||||||
|
return find(string_view_t(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator find(const string_t& key) const
|
||||||
|
{
|
||||||
|
return find(string_view_t(key.data(), key.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// whether this is an object with a member with this key
|
||||||
|
bool contains(string_view_t key) const
|
||||||
|
{
|
||||||
|
return is_object() && detail::view::find_member(*m_doc, m_node, key.data(), key.size()) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool contains(const char* key) const
|
||||||
|
{
|
||||||
|
return contains(string_view_t(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool contains(const string_t& key) const
|
||||||
|
{
|
||||||
|
return contains(string_view_t(key.data(), key.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// whether a JSON pointer can be resolved (never throws, as
|
||||||
|
/// basic_json::contains())
|
||||||
|
bool contains(const json_pointer& ptr) const
|
||||||
|
{
|
||||||
|
return static_cast<bool>(detail::view::resolve_pointer(*this, detail::json_pointer_access::reference_tokens(ptr), detail::view::pointer_mode::contains));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 1 if this is an object with a member with this key, else 0 (duplicate
|
||||||
|
/// keys count once)
|
||||||
|
size_type count(string_view_t key) const
|
||||||
|
{
|
||||||
|
return contains(key) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_type count(const char* key) const
|
||||||
|
{
|
||||||
|
return count(string_view_t(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_type count(const string_t& key) const
|
||||||
|
{
|
||||||
|
return count(string_view_t(key.data(), key.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////
|
||||||
|
// iteration //
|
||||||
|
///////////////
|
||||||
|
|
||||||
|
/// the first element or member value, in document order; a primitive
|
||||||
|
/// value is a range of one element (itself), null an empty range
|
||||||
|
NLOHMANN_VIEW_ALWAYS_INLINE iterator begin() const noexcept
|
||||||
|
{
|
||||||
|
if (NLOHMANN_VIEW_LIKELY(is_structured()))
|
||||||
|
{
|
||||||
|
return iterator(m_doc, document_data::first_child(m_node), is_object());
|
||||||
|
}
|
||||||
|
return iterator(m_doc, m_node, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
NLOHMANN_VIEW_ALWAYS_INLINE iterator end() const noexcept
|
||||||
|
{
|
||||||
|
if (NLOHMANN_VIEW_LIKELY(is_structured()))
|
||||||
|
{
|
||||||
|
return iterator(m_doc, document_data::child_end(m_node), is_object());
|
||||||
|
}
|
||||||
|
return iterator(m_doc, (is_null() || is_discarded()) ? m_node : m_node + 1, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator cbegin() const noexcept
|
||||||
|
{
|
||||||
|
return begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator cend() const noexcept
|
||||||
|
{
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// (key, value) items; the key of an array element is its index
|
||||||
|
detail::view::view_items<basic_json_view> items() const noexcept
|
||||||
|
{
|
||||||
|
return detail::view::view_items<basic_json_view>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////
|
||||||
|
// conversion //
|
||||||
|
////////////////
|
||||||
|
|
||||||
|
/// the value converted to T, as BasicJsonType::get<T>(): arithmetic types,
|
||||||
|
/// strings (string_view_t without a copy), std::nullptr_t, std::vector,
|
||||||
|
/// maps with string keys, and views are converted directly; other types
|
||||||
|
/// through materialize().get<T>()
|
||||||
|
template<typename T>
|
||||||
|
NLOHMANN_VIEW_ALWAYS_INLINE T get() const
|
||||||
|
{
|
||||||
|
return get_impl(detail::view::value_tag<T> {}, detail::priority_tag<2> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T& get_to(T& v) const
|
||||||
|
{
|
||||||
|
v = get<T>();
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the string, without a copy; valid as long as the view is. Throws
|
||||||
|
/// type_error.302 for other types.
|
||||||
|
string_view_t get_string() const
|
||||||
|
{
|
||||||
|
if (NLOHMANN_VIEW_UNLIKELY(!is_string()))
|
||||||
|
{
|
||||||
|
detail::view::throw_type_error(302, "type must be string, but is ", type_name());
|
||||||
|
}
|
||||||
|
return {m_doc->str(*m_node), m_node->len};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the text of a number as it appears in the source (e.g. "1.50", "1E2",
|
||||||
|
/// or an integer with more digits than any number type holds). Throws
|
||||||
|
/// type_error.302 for other types.
|
||||||
|
string_view_t number_token() const
|
||||||
|
{
|
||||||
|
if (NLOHMANN_VIEW_UNLIKELY(!is_number()))
|
||||||
|
{
|
||||||
|
detail::view::throw_type_error(302, "type must be number, but is ", type_name());
|
||||||
|
}
|
||||||
|
return {m_doc->str(*m_node), detail::view::number_length(*m_node)};
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////
|
/////////////////
|
||||||
// materialize //
|
// materialize //
|
||||||
/////////////////
|
/////////////////
|
||||||
@@ -237,11 +573,97 @@ class basic_json_view
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
template<typename> friend class basic_json_document;
|
template<typename> friend class basic_json_document;
|
||||||
|
friend iterator;
|
||||||
|
|
||||||
basic_json_view(const document_data* d, const node* n) noexcept
|
basic_json_view(const document_data* d, const node* n) noexcept
|
||||||
: m_doc(d), m_node(n)
|
: m_doc(d), m_node(n)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
/// the value of the first member with this key, or a discarded view
|
||||||
|
/// (object required)
|
||||||
|
NLOHMANN_VIEW_ALWAYS_INLINE basic_json_view lookup(string_view_t key) const noexcept
|
||||||
|
{
|
||||||
|
const node* const k = detail::view::find_member(*m_doc, m_node, key.data(), key.size());
|
||||||
|
return k != nullptr ? basic_json_view(m_doc, k + 1) : basic_json_view();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- get() dispatch ---
|
||||||
|
|
||||||
|
bool get_impl(detail::view::value_tag<bool> /*unused*/, detail::priority_tag<2> /*unused*/) const
|
||||||
|
{
|
||||||
|
if (NLOHMANN_VIEW_UNLIKELY(!is_boolean()))
|
||||||
|
{
|
||||||
|
detail::view::throw_type_error(302, "type must be boolean, but is ", type_name());
|
||||||
|
}
|
||||||
|
return (m_node->flags & detail::view::node_flags::is_true) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template < typename T, typename std::enable_if < std::is_arithmetic<T>::value && !std::is_same<T, bool>::value, int >::type = 0 >
|
||||||
|
NLOHMANN_VIEW_ALWAYS_INLINE T get_impl(detail::view::value_tag<T> /*unused*/, detail::priority_tag<2> /*unused*/) const
|
||||||
|
{
|
||||||
|
if (NLOHMANN_VIEW_UNLIKELY(m_node == nullptr))
|
||||||
|
{
|
||||||
|
detail::view::throw_type_error(302, "type must be number, but is ", type_name());
|
||||||
|
}
|
||||||
|
return detail::view::arithmetic_value<T, BasicJsonType>(*m_doc, *m_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::nullptr_t get_impl(detail::view::value_tag<std::nullptr_t> /*unused*/, detail::priority_tag<2> /*unused*/) const
|
||||||
|
{
|
||||||
|
if (NLOHMANN_VIEW_UNLIKELY(!is_null()))
|
||||||
|
{
|
||||||
|
detail::view::throw_type_error(302, "type must be null, but is ", type_name());
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_view_t get_impl(detail::view::value_tag<string_view_t> /*unused*/, detail::priority_tag<2> /*unused*/) const
|
||||||
|
{
|
||||||
|
return get_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Traits, typename Alloc>
|
||||||
|
std::basic_string<char, Traits, Alloc> get_impl(detail::view::value_tag<std::basic_string<char, Traits, Alloc>> /*unused*/, detail::priority_tag<2> /*unused*/) const
|
||||||
|
{
|
||||||
|
const string_view_t s = get_string();
|
||||||
|
return std::basic_string<char, Traits, Alloc>(s.data(), s.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicJsonType get_impl(detail::view::value_tag<BasicJsonType> /*unused*/, detail::priority_tag<2> /*unused*/) const
|
||||||
|
{
|
||||||
|
return materialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_json_view get_impl(detail::view::value_tag<basic_json_view> /*unused*/, detail::priority_tag<2> /*unused*/) const noexcept
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename U, typename A>
|
||||||
|
std::vector<U, A> get_impl(detail::view::value_tag<std::vector<U, A>> /*unused*/, detail::priority_tag<2> /*unused*/) const
|
||||||
|
{
|
||||||
|
return detail::view::vector_value<basic_json_view, U, A>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename K, typename V, typename C, typename A, typename std::enable_if<detail::view::is_string_key<K>::value, int>::type = 0>
|
||||||
|
std::map<K, V, C, A> get_impl(detail::view::value_tag<std::map<K, V, C, A>> /*unused*/, detail::priority_tag<2> /*unused*/) const
|
||||||
|
{
|
||||||
|
return detail::view::map_value<std::map<K, V, C, A>>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename K, typename V, typename H, typename E, typename A, typename std::enable_if<detail::view::is_string_key<K>::value, int>::type = 0>
|
||||||
|
std::unordered_map<K, V, H, E, A> get_impl(detail::view::value_tag<std::unordered_map<K, V, H, E, A>> /*unused*/, detail::priority_tag<2> /*unused*/) const
|
||||||
|
{
|
||||||
|
return detail::view::map_value<std::unordered_map<K, V, H, E, A>>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// everything else through the BasicJsonType value (from_json included)
|
||||||
|
template<typename T>
|
||||||
|
T get_impl(detail::view::value_tag<T> /*unused*/, detail::priority_tag<0> /*unused*/) const
|
||||||
|
{
|
||||||
|
return materialize().template get<T>();
|
||||||
|
}
|
||||||
|
|
||||||
const document_data* m_doc = nullptr;
|
const document_data* m_doc = nullptr;
|
||||||
const node* m_node = nullptr;
|
const node* m_node = nullptr;
|
||||||
};
|
};
|
||||||
@@ -590,6 +1012,31 @@ using ordered_json_view = basic_json_view<ordered_json>;
|
|||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|
||||||
|
// tuple protocol for the items of basic_json_view::items() (structured bindings)
|
||||||
|
namespace std // NOLINT(cert-dcl58-cpp)
|
||||||
|
{
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
// Fix: https://github.com/nlohmann/json/issues/1401
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wmismatched-tags"
|
||||||
|
#endif
|
||||||
|
template<typename View>
|
||||||
|
class tuple_size<::nlohmann::detail::view::view_item<View>> // NOLINT(cert-dcl58-cpp)
|
||||||
|
: public std::integral_constant<std::size_t, 2> {};
|
||||||
|
|
||||||
|
template<std::size_t N, typename View>
|
||||||
|
class tuple_element<N, ::nlohmann::detail::view::view_item<View>> // NOLINT(cert-dcl58-cpp)
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using type = decltype(std::declval<::nlohmann::detail::view::view_item<View>>().template get<N>());
|
||||||
|
};
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
#include <nlohmann/detail/view/macro_unscope.hpp>
|
#include <nlohmann/detail/view/macro_unscope.hpp>
|
||||||
|
|
||||||
#endif // INCLUDE_NLOHMANN_JSON_VIEW_HPP_
|
#endif // INCLUDE_NLOHMANN_JSON_VIEW_HPP_
|
||||||
|
|||||||
@@ -3380,6 +3380,39 @@ enum class value_t : std::uint8_t
|
|||||||
discarded ///< discarded by the parser callback function
|
discarded ///< discarded by the parser callback function
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief the name of a JSON type, as returned by basic_json::type_name()
|
||||||
|
|
||||||
|
Used in exception messages; also by code that reports types without a
|
||||||
|
basic_json value at hand (such as the zero-copy view).
|
||||||
|
*/
|
||||||
|
inline const char* value_type_name(const value_t t) noexcept
|
||||||
|
{
|
||||||
|
switch (t)
|
||||||
|
{
|
||||||
|
case value_t::null:
|
||||||
|
return "null";
|
||||||
|
case value_t::object:
|
||||||
|
return "object";
|
||||||
|
case value_t::array:
|
||||||
|
return "array";
|
||||||
|
case value_t::string:
|
||||||
|
return "string";
|
||||||
|
case value_t::boolean:
|
||||||
|
return "boolean";
|
||||||
|
case value_t::binary:
|
||||||
|
return "binary";
|
||||||
|
case value_t::discarded:
|
||||||
|
return "discarded";
|
||||||
|
case value_t::number_integer:
|
||||||
|
case value_t::number_unsigned:
|
||||||
|
case value_t::number_float:
|
||||||
|
return "number";
|
||||||
|
default:
|
||||||
|
return "invalid";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief comparison operator for JSON types
|
@brief comparison operator for JSON types
|
||||||
|
|
||||||
@@ -20058,6 +20091,11 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
struct json_pointer_access;
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/
|
/// @sa https://json.nlohmann.me/api/json_pointer/
|
||||||
template<typename RefStringType>
|
template<typename RefStringType>
|
||||||
@@ -20070,6 +20108,8 @@ class json_pointer
|
|||||||
template<typename>
|
template<typename>
|
||||||
friend class json_pointer;
|
friend class json_pointer;
|
||||||
|
|
||||||
|
friend struct detail::json_pointer_access;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct string_t_helper
|
struct string_t_helper
|
||||||
{
|
{
|
||||||
@@ -21198,6 +21238,20 @@ inline bool operator<(const json_pointer<RefStringTypeLhs>& lhs,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
/// the reference tokens of a json_pointer, for code that resolves pointers
|
||||||
|
/// without a basic_json value (such as the zero-copy view)
|
||||||
|
struct json_pointer_access
|
||||||
|
{
|
||||||
|
template<typename RefStringType>
|
||||||
|
static const std::vector<typename json_pointer<RefStringType>::string_t>& reference_tokens(const json_pointer<RefStringType>& ptr) noexcept
|
||||||
|
{
|
||||||
|
return ptr.reference_tokens;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|
||||||
// #include <nlohmann/detail/json_ref.hpp>
|
// #include <nlohmann/detail/json_ref.hpp>
|
||||||
@@ -32505,29 +32559,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
JSON_HEDLEY_RETURNS_NON_NULL
|
JSON_HEDLEY_RETURNS_NON_NULL
|
||||||
const char* type_name() const noexcept
|
const char* type_name() const noexcept
|
||||||
{
|
{
|
||||||
switch (m_data.m_type)
|
return detail::value_type_name(m_data.m_type);
|
||||||
{
|
|
||||||
case value_t::null:
|
|
||||||
return "null";
|
|
||||||
case value_t::object:
|
|
||||||
return "object";
|
|
||||||
case value_t::array:
|
|
||||||
return "array";
|
|
||||||
case value_t::string:
|
|
||||||
return "string";
|
|
||||||
case value_t::boolean:
|
|
||||||
return "boolean";
|
|
||||||
case value_t::binary:
|
|
||||||
return "binary";
|
|
||||||
case value_t::discarded:
|
|
||||||
return "discarded";
|
|
||||||
case value_t::number_integer:
|
|
||||||
case value_t::number_unsigned:
|
|
||||||
case value_t::number_float:
|
|
||||||
return "number";
|
|
||||||
default:
|
|
||||||
return "invalid";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_PRIVATE_UNLESS_TESTED:
|
JSON_PRIVATE_UNLESS_TESTED:
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -14,13 +14,21 @@ using nlohmann::ordered_json;
|
|||||||
using nlohmann::json_document;
|
using nlohmann::json_document;
|
||||||
using nlohmann::json_view;
|
using nlohmann::json_view;
|
||||||
using nlohmann::ordered_json_document;
|
using nlohmann::ordered_json_document;
|
||||||
|
using nlohmann::ordered_json_view;
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cmath>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iterator>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -407,7 +415,669 @@ TEST_CASE("json_view")
|
|||||||
const std::string text = R"( {"key": "value", "escaped": "a\nb", "n": 42})";
|
const std::string text = R"( {"key": "value", "escaped": "a\nb", "n": 42})";
|
||||||
const json_document d = json_document::parse(text);
|
const json_document d = json_document::parse(text);
|
||||||
CHECK(d.root().source_offset() == 2);
|
CHECK(d.root().source_offset() == 2);
|
||||||
// (element access comes with a later change; the offsets of the
|
CHECK(d.root()["key"].source_offset() == text.find("value"));
|
||||||
// string nodes are checked through materialize() above)
|
CHECK(d.root()["escaped"].source_offset() == static_cast<std::size_t>(-1));
|
||||||
|
CHECK(d.root()["n"].source_offset() == text.find("42"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
#if !defined(JSON_NOEXCEPTION)
|
||||||
|
// the exception a call throws, or "" if it throws none
|
||||||
|
template<typename F>
|
||||||
|
std::string exception_of(F f)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
catch (const json::exception& e)
|
||||||
|
{
|
||||||
|
return e.what();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// compares a view with the ordered_json value materialize() gives for it:
|
||||||
|
// types, sizes, elements and members (by index, key, and iteration), in
|
||||||
|
// document order; duplicate keys are found as their first occurrence
|
||||||
|
void check_access(const ordered_json_view& v, const ordered_json& j)
|
||||||
|
{
|
||||||
|
REQUIRE(v.type() == j.type());
|
||||||
|
CHECK(std::string(v.type_name()) == j.type_name());
|
||||||
|
if (v.is_array())
|
||||||
|
{
|
||||||
|
REQUIRE(v.size() == j.size());
|
||||||
|
std::size_t i = 0;
|
||||||
|
for (const ordered_json_view e : v)
|
||||||
|
{
|
||||||
|
CHECK(v[i].materialize() == e.materialize());
|
||||||
|
CHECK(v.at(i).materialize() == e.materialize());
|
||||||
|
check_access(e, j[i]);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
CHECK(i == v.size());
|
||||||
|
CHECK(!v[v.size()]);
|
||||||
|
std::size_t index = 0;
|
||||||
|
for (const auto& item : v.items())
|
||||||
|
{
|
||||||
|
CHECK(item.key() == std::to_string(index));
|
||||||
|
CHECK(item.value().materialize() == j[index]);
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
if (!v.empty())
|
||||||
|
{
|
||||||
|
CHECK(v.front().materialize() == j.front());
|
||||||
|
CHECK(v.back().materialize() == j.back());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (v.is_object())
|
||||||
|
{
|
||||||
|
std::vector<std::string> keys; // first occurrences, in order
|
||||||
|
std::size_t members = 0;
|
||||||
|
for (auto it = v.begin(); it != v.end(); ++it)
|
||||||
|
{
|
||||||
|
++members;
|
||||||
|
const std::string key(it.key().data(), it.key().size());
|
||||||
|
CHECK(v.contains(key));
|
||||||
|
CHECK(v.count(key) == 1);
|
||||||
|
if (std::find(keys.begin(), keys.end(), key) != keys.end())
|
||||||
|
{
|
||||||
|
continue; // a duplicate: lookups find the first one
|
||||||
|
}
|
||||||
|
keys.push_back(key);
|
||||||
|
CHECK(v.find(key) == it);
|
||||||
|
CHECK(v[key].materialize() == it->materialize());
|
||||||
|
CHECK(v.at(key).materialize() == it.value().materialize());
|
||||||
|
CHECK(v[key.c_str()].materialize() == (*it).materialize());
|
||||||
|
}
|
||||||
|
CHECK(members == v.size());
|
||||||
|
REQUIRE(keys.size() == j.size());
|
||||||
|
std::size_t k = 0;
|
||||||
|
for (const auto& member : j.items())
|
||||||
|
{
|
||||||
|
CHECK(keys[k++] == member.key());
|
||||||
|
}
|
||||||
|
if (keys.size() == members)
|
||||||
|
{
|
||||||
|
// no duplicates: the values are those of the object
|
||||||
|
for (const auto& key : keys)
|
||||||
|
{
|
||||||
|
check_access(v[key], j[key]);
|
||||||
|
}
|
||||||
|
if (!v.empty())
|
||||||
|
{
|
||||||
|
CHECK(v.front().materialize() == j.front());
|
||||||
|
CHECK(v.back().materialize() == j.back());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CHECK(!v["not a key in the generated documents"]);
|
||||||
|
CHECK(v.find("not a key in the generated documents") == v.end());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// a primitive is a range of one element; null is empty
|
||||||
|
CHECK(static_cast<std::size_t>(std::distance(v.begin(), v.end())) == (v.is_null() ? 0u : 1u));
|
||||||
|
if (!v.is_null())
|
||||||
|
{
|
||||||
|
CHECK((*v.begin()).materialize() == j);
|
||||||
|
CHECK(v.front().materialize() == j);
|
||||||
|
CHECK(v.back().materialize() == j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("json_view element access and iteration")
|
||||||
|
{
|
||||||
|
SECTION("generated documents")
|
||||||
|
{
|
||||||
|
generator g;
|
||||||
|
for (int i = 0; i < 2000; ++i)
|
||||||
|
{
|
||||||
|
std::string text;
|
||||||
|
g.value(text, 0);
|
||||||
|
CAPTURE(text);
|
||||||
|
const ordered_json_document d = ordered_json_document::parse(text);
|
||||||
|
check_access(d.root(), ordered_json::parse(text));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("keys")
|
||||||
|
{
|
||||||
|
// keys of every length around the 2/4/8/16-byte loads, with escapes
|
||||||
|
std::string text = "{";
|
||||||
|
std::vector<std::string> keys = {"", "x"};
|
||||||
|
for (std::size_t n = 1; n <= 40; ++n)
|
||||||
|
{
|
||||||
|
keys.emplace_back(n, 'k');
|
||||||
|
keys.push_back(std::string(n, 'k') + "x");
|
||||||
|
keys.push_back("x" + std::string(n, 'k'));
|
||||||
|
}
|
||||||
|
for (std::size_t i = 0; i < keys.size(); ++i)
|
||||||
|
{
|
||||||
|
text += (i != 0 ? ",\"" : "\"") + keys[i] + "\":" + std::to_string(i);
|
||||||
|
}
|
||||||
|
text += ",\"esc\\u0061ped\":\"escaped key\"}"; // NOLINT(modernize-raw-string-literal)
|
||||||
|
const json_document d = json_document::parse(text);
|
||||||
|
const json_view root = d.root();
|
||||||
|
for (std::size_t i = 0; i < keys.size(); ++i)
|
||||||
|
{
|
||||||
|
CAPTURE(keys[i]);
|
||||||
|
CHECK(root[keys[i]].materialize() == i);
|
||||||
|
CHECK(root.at(keys[i]).materialize() == i);
|
||||||
|
CHECK(root.find(keys[i]).key() == keys[i]);
|
||||||
|
CHECK(!root.contains(keys[i] + "y"));
|
||||||
|
}
|
||||||
|
CHECK(root["escaped"].materialize() == "escaped key");
|
||||||
|
CHECK(!root.contains("esc\\u0061ped"));
|
||||||
|
#ifdef JSON_HAS_CPP_17
|
||||||
|
CHECK(root[std::string_view("kkk")].materialize() == root["kkk"].materialize());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("duplicate keys: lookups find the first member, iteration all")
|
||||||
|
{
|
||||||
|
const json_document d = json_document::parse(R"({"a":1,"b":2,"a":3})");
|
||||||
|
const json_view v = d.root();
|
||||||
|
CHECK(v.size() == 3);
|
||||||
|
CHECK(v["a"].materialize() == 1);
|
||||||
|
CHECK(v.at("a").materialize() == 1);
|
||||||
|
CHECK(v.find("a") == v.begin());
|
||||||
|
CHECK(v.count("a") == 1);
|
||||||
|
std::string order;
|
||||||
|
for (auto it = v.begin(); it != v.end(); ++it)
|
||||||
|
{
|
||||||
|
order += std::string(it.key().data(), it.key().size()) + it->materialize().dump();
|
||||||
|
}
|
||||||
|
CHECK(order == "a1b2a3");
|
||||||
|
CHECK(v.back().materialize() == 3);
|
||||||
|
CHECK(v.materialize() == json::parse(R"({"a":1,"b":2,"a":3})")); // the last value, as parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("errors are those of const basic_json")
|
||||||
|
{
|
||||||
|
for (const char* text :
|
||||||
|
{"null", "true", "42", "-1", "1.5", "\"s\"", "[]", "[1,2]", "{}", "{\"a\":1}"
|
||||||
|
})
|
||||||
|
{
|
||||||
|
CAPTURE(text);
|
||||||
|
const json_document d = json_document::parse(text);
|
||||||
|
const json_view v = d.root();
|
||||||
|
const json j = v.materialize();
|
||||||
|
#if !defined(JSON_NOEXCEPTION)
|
||||||
|
if (!j.is_object())
|
||||||
|
{
|
||||||
|
CHECK(exception_of([&] { static_cast<void>(v["a"]); }) == exception_of([&] { static_cast<void>(j["a"]); }));
|
||||||
|
}
|
||||||
|
if (!j.is_array())
|
||||||
|
{
|
||||||
|
CHECK(exception_of([&] { static_cast<void>(v[0]); }) == exception_of([&] { static_cast<void>(j[0]); }));
|
||||||
|
}
|
||||||
|
CHECK(exception_of([&] { static_cast<void>(v.at("a")); }) == exception_of([&] { static_cast<void>(j.at("a")); }));
|
||||||
|
CHECK(exception_of([&] { static_cast<void>(v.at("missing")); }) == exception_of([&] { static_cast<void>(j.at("missing")); }));
|
||||||
|
CHECK(exception_of([&] { static_cast<void>(v.at(0)); }) == exception_of([&] { static_cast<void>(j.at(0)); }));
|
||||||
|
CHECK(exception_of([&] { static_cast<void>(v.at(5)); }) == exception_of([&] { static_cast<void>(j.at(5)); }));
|
||||||
|
if (!(j.is_object() && j.empty())) // (key() of an end iterator)
|
||||||
|
{
|
||||||
|
CHECK(exception_of([&] { static_cast<void>(v.begin().key()); }) == exception_of([&] { static_cast<void>(j.begin().key()); }));
|
||||||
|
}
|
||||||
|
if (!j.empty() || j.is_null())
|
||||||
|
{
|
||||||
|
CHECK(exception_of([&] { static_cast<void>(v.front()); }) == exception_of([&] { static_cast<void>(j.front()); }));
|
||||||
|
CHECK(exception_of([&] { static_cast<void>(v.back()); }) == exception_of([&] { static_cast<void>(j.back()); }));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
CHECK(v.contains("a") == j.contains("a"));
|
||||||
|
CHECK(v.count("a") == j.count("a"));
|
||||||
|
CHECK((v.find("a") == v.end()) == (j.find("a") == j.end())); // NOLINT(readability-container-contains): find() is what is tested
|
||||||
|
}
|
||||||
|
|
||||||
|
// where basic_json has undefined behavior, the view answers safely
|
||||||
|
const json_document d = json_document::parse(R"({"a":[]})");
|
||||||
|
CHECK(!d.root()["b"]);
|
||||||
|
CHECK(!d.root()["a"][0]);
|
||||||
|
CHECK_THROWS_WITH_AS(d.root()["a"].front(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||||
|
CHECK_THROWS_WITH_AS(d.root()["a"].back(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||||
|
const json_view invalid{};
|
||||||
|
CHECK(invalid.begin() == invalid.end());
|
||||||
|
CHECK(std::string(invalid.type_name()) == "discarded");
|
||||||
|
CHECK_THROWS_WITH_AS(invalid["a"], "[json.exception.type_error.305] cannot use operator[] with a string argument with discarded", json::type_error&);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("iterators")
|
||||||
|
{
|
||||||
|
const json_document d = json_document::parse(R"({"x":[1,{"y":2}],"z":null})");
|
||||||
|
const json_view v = d.root();
|
||||||
|
json_view::iterator it = v.begin();
|
||||||
|
CHECK(it.is_object_iterator());
|
||||||
|
CHECK(it->is_array());
|
||||||
|
CHECK(it->size() == 2);
|
||||||
|
const json_view::iterator previous = it++;
|
||||||
|
CHECK(previous.key() == "x");
|
||||||
|
CHECK(it.key() == "z");
|
||||||
|
CHECK(it.value().is_null());
|
||||||
|
CHECK(++it == v.end());
|
||||||
|
CHECK(v.cbegin() == v.begin());
|
||||||
|
CHECK(v.cend() == v.end());
|
||||||
|
CHECK(!v["x"].begin().is_object_iterator());
|
||||||
|
CHECK(json_view::iterator() == json_view::iterator());
|
||||||
|
// standard algorithms
|
||||||
|
CHECK(std::count_if(v["x"].begin(), v["x"].end(), [](const json_view & e)
|
||||||
|
{
|
||||||
|
return e.is_object();
|
||||||
|
}) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("items")
|
||||||
|
{
|
||||||
|
const json_document d = json_document::parse(R"({"a":1,"b":[true,false]})");
|
||||||
|
std::string keys;
|
||||||
|
for (const auto& item : d.root().items())
|
||||||
|
{
|
||||||
|
keys += std::string(item.key().data(), item.key().size());
|
||||||
|
CHECK(item.value().materialize() == d.root()[item.key()].materialize());
|
||||||
|
}
|
||||||
|
CHECK(keys == "ab");
|
||||||
|
auto items = d.root()["b"].items();
|
||||||
|
auto first = items.begin();
|
||||||
|
CHECK((*first++).key() == "0");
|
||||||
|
CHECK((*first).key() == "1");
|
||||||
|
CHECK(++first == items.end());
|
||||||
|
#ifdef JSON_HAS_CPP_17
|
||||||
|
std::string pairs;
|
||||||
|
for (const auto [key, value] : d.root().items())
|
||||||
|
{
|
||||||
|
pairs += std::string(key) + "=" + value.materialize().dump() + ";";
|
||||||
|
}
|
||||||
|
CHECK(pairs == "a=1;b=[true,false];");
|
||||||
|
static_assert(std::tuple_size<json_view::item>::value == 2, "");
|
||||||
|
static_assert(std::is_same<std::tuple_element<1, json_view::item>::type, json_view>::value, "");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
#if !defined(JSON_NOEXCEPTION)
|
||||||
|
// an exception message without the context that basic_json adds with
|
||||||
|
// JSON_DIAGNOSTICS ("(/path) ") and JSON_DIAGNOSTIC_POSITIONS ("(bytes 1-2) ");
|
||||||
|
// the view's exceptions have no such context
|
||||||
|
std::string without_path(std::string msg)
|
||||||
|
{
|
||||||
|
for (const char* prefix :
|
||||||
|
{"] (/", "] (bytes "
|
||||||
|
})
|
||||||
|
{
|
||||||
|
const std::size_t open = msg.find(prefix);
|
||||||
|
if (open != std::string::npos)
|
||||||
|
{
|
||||||
|
msg.erase(open + 2, msg.find(") ", open) + 2 - (open + 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// the bits of a float, to compare values bit for bit
|
||||||
|
std::uint64_t bits(double x)
|
||||||
|
{
|
||||||
|
std::uint64_t r = 0;
|
||||||
|
std::memcpy(&r, &x, sizeof(r));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t bits(float x)
|
||||||
|
{
|
||||||
|
std::uint32_t r = 0;
|
||||||
|
std::memcpy(&r, &x, sizeof(r));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_duplicate_keys(const ordered_json_view& v)
|
||||||
|
{
|
||||||
|
if (v.is_object() && v.size() != v.materialize().size())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return std::any_of(v.begin(), v.end(), [](const ordered_json_view e)
|
||||||
|
{
|
||||||
|
return e.is_structured() && has_duplicate_keys(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// compares the conversions of a view with those of ordered_json
|
||||||
|
void check_values(const ordered_json_view& v, const ordered_json& j, const std::string& text)
|
||||||
|
{
|
||||||
|
CHECK(v.get<ordered_json>() == j);
|
||||||
|
switch (j.type())
|
||||||
|
{
|
||||||
|
case json::value_t::number_integer:
|
||||||
|
case json::value_t::number_unsigned:
|
||||||
|
case json::value_t::number_float:
|
||||||
|
{
|
||||||
|
// (converting a float out of range of the target type is undefined)
|
||||||
|
if (j.is_number_unsigned())
|
||||||
|
{
|
||||||
|
CHECK(v.get<std::uint64_t>() == j.get<std::uint64_t>());
|
||||||
|
}
|
||||||
|
else if (j.is_number_integer())
|
||||||
|
{
|
||||||
|
CHECK(v.get<std::int64_t>() == j.get<std::int64_t>());
|
||||||
|
}
|
||||||
|
CHECK(bits(v.get<double>()) == bits(j.get<double>()));
|
||||||
|
if (std::abs(j.get<double>()) < 1e9)
|
||||||
|
{
|
||||||
|
CHECK(v.get<int>() == j.get<int>());
|
||||||
|
}
|
||||||
|
const auto token = v.number_token();
|
||||||
|
CHECK(text.compare(v.source_offset(), token.size(), token.data(), token.size()) == 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case json::value_t::string:
|
||||||
|
CHECK(v.get<std::string>() == j.get<std::string>());
|
||||||
|
CHECK(std::string(v.get_string().data(), v.get_string().size()) == j.get<std::string>());
|
||||||
|
break;
|
||||||
|
case json::value_t::boolean:
|
||||||
|
CHECK(v.get<bool>() == j.get<bool>());
|
||||||
|
CHECK(v.get<int>() == j.get<int>());
|
||||||
|
break;
|
||||||
|
case json::value_t::null:
|
||||||
|
CHECK(v.get<std::nullptr_t>() == nullptr);
|
||||||
|
break;
|
||||||
|
case json::value_t::array:
|
||||||
|
CHECK(v.get<std::vector<ordered_json>>() == j.get<std::vector<ordered_json>>());
|
||||||
|
break;
|
||||||
|
case json::value_t::object:
|
||||||
|
CHECK((v.get<std::map<std::string, ordered_json>>() == j.get<std::map<std::string, ordered_json>>()));
|
||||||
|
break;
|
||||||
|
case json::value_t::binary:
|
||||||
|
case json::value_t::discarded:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(JSON_NOEXCEPTION)
|
||||||
|
// conversions to the wrong type throw what basic_json throws
|
||||||
|
if (!j.is_number())
|
||||||
|
{
|
||||||
|
CHECK(exception_of([&] { static_cast<void>(v.get<int>()); }) == without_path(exception_of([&] { static_cast<void>(j.get<int>()); })));
|
||||||
|
}
|
||||||
|
CHECK(exception_of([&] { static_cast<void>(v.get<bool>()); }) == without_path(exception_of([&] { static_cast<void>(j.get<bool>()); })));
|
||||||
|
CHECK(exception_of([&] { static_cast<void>(v.get<std::string>()); }) == without_path(exception_of([&] { static_cast<void>(j.get<std::string>()); })));
|
||||||
|
CHECK(exception_of([&] { static_cast<void>(v.get<std::nullptr_t>()); }) == without_path(exception_of([&] { static_cast<void>(j.get<std::nullptr_t>()); })));
|
||||||
|
if (!j.is_array())
|
||||||
|
{
|
||||||
|
CHECK(exception_of([&] { static_cast<void>(v.get<std::vector<int>>()); }) == without_path(exception_of([&] { static_cast<void>(j.get<std::vector<int>>()); })));
|
||||||
|
}
|
||||||
|
if (!j.is_object())
|
||||||
|
{
|
||||||
|
CHECK(exception_of([&] { static_cast<void>(v.get<std::map<std::string, int>>()); }) == without_path(exception_of([&] { static_cast<void>(j.get<std::map<std::string, int>>()); })));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (v.is_array())
|
||||||
|
{
|
||||||
|
std::size_t i = 0;
|
||||||
|
for (const ordered_json_view e : v)
|
||||||
|
{
|
||||||
|
check_values(e, j[i++], text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (v.is_object())
|
||||||
|
{
|
||||||
|
for (auto it = v.begin(); it != v.end(); ++it)
|
||||||
|
{
|
||||||
|
const std::string key(it.key().data(), it.key().size());
|
||||||
|
if (v.size() == j.size()) // (no duplicate keys)
|
||||||
|
{
|
||||||
|
check_values(it.value(), j[key], text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct record
|
||||||
|
{
|
||||||
|
std::string name{}; // NOLINT(readability-redundant-member-init)
|
||||||
|
int count = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void from_json(const json& j, record& r)
|
||||||
|
{
|
||||||
|
j.at("name").get_to(r.name);
|
||||||
|
j.at("count").get_to(r.count);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("json_view values")
|
||||||
|
{
|
||||||
|
SECTION("generated documents")
|
||||||
|
{
|
||||||
|
generator g;
|
||||||
|
for (int i = 0; i < 2000; ++i)
|
||||||
|
{
|
||||||
|
std::string text;
|
||||||
|
g.value(text, 0);
|
||||||
|
CAPTURE(text);
|
||||||
|
const ordered_json_document d = ordered_json_document::parse(text);
|
||||||
|
check_values(d.root(), ordered_json::parse(text), text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("floats are converted as parse() converts them")
|
||||||
|
{
|
||||||
|
std::mt19937_64 rng(5295); // NOLINT(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed)
|
||||||
|
std::vector<std::string> tokens = {"0.1", "-0.0", "1e308", "1.7976931348623157e308", "2.2250738585072011e-308", "4.9e-324", "5e-324",
|
||||||
|
"0.1000000000000000055511151231257827021181583404541015625", "123456789012345678901234567890",
|
||||||
|
"9007199254740993", "1.00000000000000011102230246251565404236316680908203125", "7.2057594037927933e16"
|
||||||
|
};
|
||||||
|
for (int i = 0; i < 20000; ++i)
|
||||||
|
{
|
||||||
|
const std::uint64_t bits = rng();
|
||||||
|
double d = 0;
|
||||||
|
std::memcpy(&d, &bits, sizeof(d));
|
||||||
|
if (!std::isfinite(d))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::array<char, 400> buf{};
|
||||||
|
switch (i % 5) // NOLINT(hicpp-multiway-paths-covered)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
std::snprintf(buf.data(), buf.size(), "%.17g", d); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
std::snprintf(buf.data(), buf.size(), "%.15g", d); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
std::snprintf(buf.data(), buf.size(), "%.3e", d); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
std::snprintf(buf.data(), buf.size(), "%.25g", d); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
std::snprintf(buf.data(), buf.size(), "%.0f", d); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tokens.emplace_back(buf.data());
|
||||||
|
}
|
||||||
|
using json_float = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, float>;
|
||||||
|
for (const auto& token : tokens)
|
||||||
|
{
|
||||||
|
CAPTURE(token);
|
||||||
|
const std::string text = "[" + token + "]";
|
||||||
|
const double b = json::parse(text)[0].get<double>();
|
||||||
|
CHECK(bits(json_document::parse(text).root()[0].get<double>()) == bits(b));
|
||||||
|
if (std::abs(b) < 1e38)
|
||||||
|
{
|
||||||
|
CHECK(bits(nlohmann::basic_json_document<json_float>::parse(text).root()[0].get<float>()) == bits(json_float::parse(text)[0].get<float>()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("number tokens")
|
||||||
|
{
|
||||||
|
const json_document d = json_document::parse(R"([1.50, 1E2, -0, 123456789012345678901234567890, -12, 7, "x"])");
|
||||||
|
const json_view v = d.root();
|
||||||
|
CHECK(v[0].number_token() == "1.50");
|
||||||
|
CHECK(v[1].number_token() == "1E2");
|
||||||
|
CHECK(v[2].number_token() == "-0");
|
||||||
|
CHECK(v[3].number_token() == "123456789012345678901234567890");
|
||||||
|
CHECK(v[4].number_token() == "-12");
|
||||||
|
CHECK(v[5].number_token() == "7");
|
||||||
|
CHECK_THROWS_WITH_AS(v[6].number_token(), "[json.exception.type_error.302] type must be number, but is string", json::type_error&);
|
||||||
|
CHECK_THROWS_WITH_AS(v.get_string(), "[json.exception.type_error.302] type must be string, but is array", json::type_error&);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("conversions")
|
||||||
|
{
|
||||||
|
const std::string text = R"({"name": "widget", "count": 3, "tags": ["a", "b\n"], "sizes": {"s": 1, "m": 2}, "pair": [1, "x"]})";
|
||||||
|
const json_document d = json_document::parse(text);
|
||||||
|
const json_view v = d.root();
|
||||||
|
const json j = json::parse(text);
|
||||||
|
|
||||||
|
// user types with from_json, and other types, through basic_json
|
||||||
|
const record r = v.get<record>();
|
||||||
|
CHECK(r.name == "widget");
|
||||||
|
CHECK(r.count == 3);
|
||||||
|
CHECK((v["pair"].get<std::pair<int, std::string>>() == j["pair"].get<std::pair<int, std::string>>()));
|
||||||
|
CHECK(v["tags"].get<std::list<std::string>>() == j["tags"].get<std::list<std::string>>());
|
||||||
|
CHECK((v["sizes"].get<std::unordered_map<std::string, int>>() == j["sizes"].get<std::unordered_map<std::string, int>>()));
|
||||||
|
CHECK(v["tags"].get<std::vector<std::string>>() == std::vector<std::string> {"a", "b\n"});
|
||||||
|
|
||||||
|
// views of the elements
|
||||||
|
const auto views = v["tags"].get<std::vector<json_view>>();
|
||||||
|
CHECK(views.size() == 2);
|
||||||
|
CHECK(views[1].get_string() == "b\n");
|
||||||
|
const auto members = v.get<std::map<std::string, json_view>>();
|
||||||
|
CHECK(members.at("count").get<int>() == 3);
|
||||||
|
CHECK(v.get<json_view>()["name"].get_string() == "widget");
|
||||||
|
|
||||||
|
// strings without a copy point into the source text
|
||||||
|
CHECK(v["name"].get_string().data() == text.data() + text.find("widget"));
|
||||||
|
#ifdef JSON_HAS_CPP_17
|
||||||
|
CHECK(v["name"].get<std::string_view>() == "widget");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
int count = 0;
|
||||||
|
CHECK(&v["name"].get_to(name) == &name);
|
||||||
|
v["count"].get_to(count);
|
||||||
|
CHECK(name == "widget");
|
||||||
|
CHECK(count == 3);
|
||||||
|
|
||||||
|
// a duplicate key: the last value, as parse()
|
||||||
|
CHECK((json_document::parse(R"({"a":1,"a":2})").root().get<std::map<std::string, int>>() == std::map<std::string, int> {{"a", 2}}));
|
||||||
|
|
||||||
|
const json_view invalid{};
|
||||||
|
CHECK_THROWS_WITH_AS(invalid.get<int>(), "[json.exception.type_error.302] type must be number, but is discarded", json::type_error&);
|
||||||
|
CHECK(invalid.get<json>().is_discarded());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("value")
|
||||||
|
{
|
||||||
|
const json_document d = json_document::parse(R"({"n": 1, "s": "text", "o": {"x": [10, 20]}})");
|
||||||
|
const json_view v = d.root();
|
||||||
|
const json j = v.materialize();
|
||||||
|
CHECK(v.value("n", 0) == j.value("n", 0));
|
||||||
|
CHECK(v.value("missing", 42) == j.value("missing", 42));
|
||||||
|
CHECK(v.value("s", "default") == j.value("s", "default"));
|
||||||
|
CHECK(v.value("missing", "default") == j.value("missing", "default"));
|
||||||
|
CHECK(v.value(std::string("n"), 2.5) == j.value(std::string("n"), 2.5));
|
||||||
|
CHECK(v.value(json::json_pointer("/o/x/1"), 0) == j.value(json::json_pointer("/o/x/1"), 0));
|
||||||
|
CHECK(v.value(json::json_pointer("/o/x/5"), 0) == j.value(json::json_pointer("/o/x/5"), 0));
|
||||||
|
CHECK(v.value(json::json_pointer("/o/y"), "none") == j.value(json::json_pointer("/o/y"), "none"));
|
||||||
|
// with a JSON pointer, arrays can be asked as well
|
||||||
|
CHECK(v["o"]["x"].value(json::json_pointer("/1"), 0) == j["o"]["x"].value(json::json_pointer("/1"), 0));
|
||||||
|
CHECK(v["o"]["x"].value(json::json_pointer("/7"), 3) == j["o"]["x"].value(json::json_pointer("/7"), 3));
|
||||||
|
#if !defined(JSON_NOEXCEPTION)
|
||||||
|
CHECK(exception_of([&] { static_cast<void>(v["o"]["x"].value("k", 0)); }) == without_path(exception_of([&] { static_cast<void>(j["o"]["x"].value("k", 0)); })));
|
||||||
|
CHECK(exception_of([&] { static_cast<void>(v.value("s", 0)); }) == without_path(exception_of([&] { static_cast<void>(j.value("s", 0)); })));
|
||||||
|
CHECK(exception_of([&] { static_cast<void>(v["n"].value("x", 0)); }) == without_path(exception_of([&] { static_cast<void>(j["n"].value("x", 0)); })));
|
||||||
|
CHECK(exception_of([&] { static_cast<void>(v["n"].value(json::json_pointer("/x"), 0)); }) == without_path(exception_of([&] { static_cast<void>(j["n"].value(json::json_pointer("/x"), 0)); })));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("json_view JSON pointers")
|
||||||
|
{
|
||||||
|
SECTION("every value of generated documents")
|
||||||
|
{
|
||||||
|
generator g;
|
||||||
|
for (int i = 0; i < 1000; ++i)
|
||||||
|
{
|
||||||
|
std::string text;
|
||||||
|
g.value(text, 0);
|
||||||
|
const ordered_json_document d = ordered_json_document::parse(text);
|
||||||
|
if (has_duplicate_keys(d.root()))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
CAPTURE(text);
|
||||||
|
const ordered_json j = ordered_json::parse(text);
|
||||||
|
const ordered_json flat = j.flatten();
|
||||||
|
for (const auto& leaf : flat.items())
|
||||||
|
{
|
||||||
|
// the leaf and each of its parents
|
||||||
|
for (ordered_json::json_pointer p(leaf.key());; p = p.parent_pointer())
|
||||||
|
{
|
||||||
|
CAPTURE(p.to_string());
|
||||||
|
CHECK(d.root()[p].materialize() == j[p]);
|
||||||
|
CHECK(d.root().at(p).materialize() == j.at(p));
|
||||||
|
CHECK(d.root().contains(p));
|
||||||
|
if (p.empty())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(JSON_NOEXCEPTION)
|
||||||
|
SECTION("errors are those of basic_json")
|
||||||
|
{
|
||||||
|
const std::string text = R"({"a": [1, {"b": null}], "c": "s", "": {"": 0}, "a~b": 1, "c/d": 2})";
|
||||||
|
const json_document d = json_document::parse(text);
|
||||||
|
const json_view v = d.root();
|
||||||
|
const json j = v.materialize();
|
||||||
|
for (const char* pointer :
|
||||||
|
{"", "/", "//", "/a", "/a/0", "/a/1/b", "/a/-", "/a/01", "/a/00", "/a/1a", "/a/a", "/a/", "/a/2", "/a/99", "/a/99999999999999999999",
|
||||||
|
"/a/18446744073709551615", "/a/-1", "/a/+1", "/a/ 1", "/x", "/c/x", "/a/0/x", "/a/1/b/c", "/a~0b", "/c~1d", "/c~1d/x", "/a/1/-"
|
||||||
|
})
|
||||||
|
{
|
||||||
|
CAPTURE(pointer);
|
||||||
|
const json::json_pointer p(pointer);
|
||||||
|
const std::string at_error = without_path(exception_of([&] { static_cast<void>(j.at(p)); }));
|
||||||
|
CHECK(exception_of([&] { static_cast<void>(v.at(p)); }) == at_error);
|
||||||
|
if (at_error.empty())
|
||||||
|
{
|
||||||
|
CHECK(v.at(p).materialize() == j.at(p));
|
||||||
|
CHECK(v[p].materialize() == j[p]);
|
||||||
|
}
|
||||||
|
else if (at_error.find("out_of_range.401") != std::string::npos || at_error.find("out_of_range.403") != std::string::npos) // NOLINT(abseil-string-find-str-contains)
|
||||||
|
{
|
||||||
|
// undefined behavior for const basic_json::operator[]
|
||||||
|
CHECK(!v[p]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CHECK(exception_of([&] { static_cast<void>(v[p]); }) == without_path(exception_of([&] { static_cast<void>(j[p]); })));
|
||||||
|
}
|
||||||
|
// (basic_json::contains() throws out_of_range.404 for an empty
|
||||||
|
// array index token, although it is not meant to throw; the view
|
||||||
|
// answers false)
|
||||||
|
const std::string contains_error = exception_of([&]
|
||||||
|
{
|
||||||
|
const bool found = j.contains(p);
|
||||||
|
static_cast<void>(found);
|
||||||
|
});
|
||||||
|
CHECK(v.contains(p) == (contains_error.empty() && j.contains(p)));
|
||||||
|
CHECK(exception_of([&] { static_cast<void>(v.value(p, 5)); }) == without_path(exception_of([&] { static_cast<void>(j.value(p, 5)); })));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user