mirror of
https://github.com/nlohmann/json.git
synced 2026-09-30 11:40:30 +00:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2037f52b9 | ||
|
|
dbbe96c42c | ||
|
|
bba3fa0307 | ||
|
|
9549dce77b | ||
|
|
a210437b30 | ||
|
|
fdcba786d8 | ||
|
|
2aedf52d54 | ||
|
|
bc36fd323b | ||
|
|
9856da2561 | ||
|
|
85f414ae7a | ||
|
|
358970ee7f |
@@ -77,8 +77,10 @@ cc_library(
|
||||
"include/nlohmann/detail/view/materialize.hpp",
|
||||
"include/nlohmann/detail/view/node.hpp",
|
||||
"include/nlohmann/detail/view/number.hpp",
|
||||
"include/nlohmann/detail/view/pointer.hpp",
|
||||
"include/nlohmann/detail/view/scan.hpp",
|
||||
"include/nlohmann/detail/view/string_ref.hpp",
|
||||
"include/nlohmann/detail/view/value.hpp",
|
||||
"include/nlohmann/json.hpp",
|
||||
"include/nlohmann/json_fwd.hpp",
|
||||
"include/nlohmann/json_view.hpp",
|
||||
|
||||
@@ -154,6 +154,9 @@ INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::empty', 'Met
|
||||
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_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');
|
||||
@@ -169,12 +172,14 @@ INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::is_string',
|
||||
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::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[]', '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::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_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_document', 'Class', 'api/json_document/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_view', 'Class', 'api/json_view/index.html');
|
||||
|
||||
@@ -163,6 +163,8 @@ overload (3).
|
||||
- [get_ref](get_ref.md) get a reference to the stored value
|
||||
- [operator ValueType](operator_ValueType.md) get a value via implicit conversion
|
||||
- [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
|
||||
|
||||
|
||||
@@ -61,6 +61,8 @@ Constant.
|
||||
## See also
|
||||
|
||||
- [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
|
||||
|
||||
|
||||
@@ -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_ptr](get_ptr.md) get a pointer to the stored value
|
||||
- [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
|
||||
|
||||
|
||||
@@ -181,6 +181,7 @@ changes to any JSON value.
|
||||
|
||||
- see [`at`](at.md) for access by reference with range checking
|
||||
- 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
|
||||
|
||||
|
||||
@@ -9,11 +9,15 @@ 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
|
||||
|
||||
@@ -23,10 +27,14 @@ basic_json_view at(int idx) const;
|
||||
`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
|
||||
|
||||
@@ -42,6 +50,20 @@ Strong exception safety: if an exception is thrown, there are no changes to the
|
||||
[`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
|
||||
@@ -54,16 +76,20 @@ None of these exceptions carry a [`JSON_DIAGNOSTICS`](../macros/json_diagnostics
|
||||
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.
|
||||
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 "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
|
||||
@@ -79,11 +105,27 @@ existing error handling written against `BasicJsonType::at` keeps working unchan
|
||||
--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
|
||||
|
||||
|
||||
@@ -1,21 +1,30 @@
|
||||
# <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;
|
||||
```
|
||||
|
||||
Checks whether the value is an object with a member with key `key`.
|
||||
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
|
||||
|
||||
`#!cpp true` if the value is an object and has a member with key `key`, `#!cpp false` otherwise.
|
||||
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
|
||||
|
||||
@@ -23,22 +32,34 @@ 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.
|
||||
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
|
||||
|
||||
This method always returns `#!cpp false` when the value is not an object -- including a [discarded](is_discarded.md)
|
||||
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(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 "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.
|
||||
@@ -53,10 +74,26 @@ view.
|
||||
--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
|
||||
|
||||
@@ -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.
|
||||
@@ -20,8 +20,11 @@ Moving the document itself does not invalidate its views: the index is heap-allo
|
||||
`basic_json_document` object.
|
||||
|
||||
`basic_json_view` provides the read-only part of the `BasicJsonType` interface: the type-inspection functions, element
|
||||
access, lookup, iteration, and [`materialize()`](materialize.md) to build the `BasicJsonType` value of a subtree on
|
||||
demand. It does not (yet) provide `get<T>()`, JSON Pointer support, `dump()`, or comparison.
|
||||
access, lookup, iteration, and conversion -- [`get<T>()`](get.md), [`get_string()`](get_string.md),
|
||||
[`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
|
||||
|
||||
@@ -72,6 +75,7 @@ demand. It does not (yet) provide `get<T>()`, JSON Pointer support, `dump()`, or
|
||||
|
||||
- [**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
|
||||
|
||||
@@ -96,6 +100,10 @@ demand. It does not (yet) provide `get<T>()`, JSON Pointer support, `dump()`, or
|
||||
|
||||
### 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
|
||||
|
||||
### Source access
|
||||
|
||||
@@ -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.
|
||||
@@ -9,12 +9,18 @@ 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
|
||||
|
||||
@@ -24,11 +30,18 @@ basic_json_view operator[](int idx) const;
|
||||
`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
|
||||
|
||||
@@ -42,9 +55,19 @@ Strong exception safety: if an exception is thrown, there are no changes to the
|
||||
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.
|
||||
|
||||
Neither exception carries 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
|
||||
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
|
||||
@@ -55,6 +78,8 @@ value to point at, so the exception is created without one, even if `BasicJsonTy
|
||||
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
|
||||
|
||||
@@ -74,9 +99,18 @@ document.
|
||||
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 "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
|
||||
@@ -93,12 +127,29 @@ document.
|
||||
--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
|
||||
|
||||
|
||||
@@ -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,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,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,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,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,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,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
|
||||
@@ -139,9 +139,33 @@ whenever any of the other conditions above was not met.
|
||||
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.
|
||||
- **`get<T>()`, JSON Pointer, `dump()`, and comparison are not (yet) provided** by `basic_json_view`. For now,
|
||||
- **`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`
|
||||
|
||||
| | [`json`](../api/json.md) / [`ordered_json`](../api/ordered_json.md) | [SAX interface](parsing/sax_interface.md) | [`json_document`](../api/json_document.md) / [`json_view`](../api/json_view.md) |
|
||||
|
||||
@@ -257,6 +257,9 @@ nav:
|
||||
- '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_binary': api/basic_json_view/is_binary.md
|
||||
- 'is_boolean': api/basic_json_view/is_boolean.md
|
||||
@@ -272,12 +275,14 @@ nav:
|
||||
- 'is_structured': api/basic_json_view/is_structured.md
|
||||
- 'items': api/basic_json_view/items.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[]': api/basic_json_view/operator[].md
|
||||
- 'size': api/basic_json_view/size.md
|
||||
- 'source_offset': api/basic_json_view/source_offset.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:
|
||||
- 'Overview': api/byte_container_with_subtype/index.md
|
||||
- '(constructor)': api/byte_container_with_subtype/byte_container_with_subtype.md
|
||||
|
||||
@@ -30,6 +30,11 @@
|
||||
|
||||
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
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/
|
||||
template<typename RefStringType>
|
||||
@@ -42,6 +47,8 @@ class json_pointer
|
||||
template<typename>
|
||||
friend class json_pointer;
|
||||
|
||||
friend struct detail::json_pointer_access;
|
||||
|
||||
template<typename T>
|
||||
struct string_t_helper
|
||||
{
|
||||
@@ -1164,4 +1171,18 @@ inline bool operator<(const json_pointer<RefStringTypeLhs>& lhs,
|
||||
}
|
||||
#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
|
||||
|
||||
@@ -40,6 +40,12 @@ namespace view
|
||||
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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -27,11 +27,14 @@
|
||||
#include <cstddef> // size_t
|
||||
#include <cstring> // memcpy, strlen
|
||||
#include <iterator> // distance, input_iterator_tag, iterator_traits
|
||||
#include <map> // map
|
||||
#include <memory> // unique_ptr
|
||||
#include <string> // string
|
||||
#include <tuple> // tuple_element, tuple_size
|
||||
#include <type_traits> // enable_if, integral_constant, is_base_of, is_integral, is_same, remove_cv, remove_extent
|
||||
#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 <vector> // vector
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -49,7 +52,9 @@
|
||||
#include <nlohmann/detail/view/macro_scope.hpp>
|
||||
#include <nlohmann/detail/view/materialize.hpp>
|
||||
#include <nlohmann/detail/view/node.hpp>
|
||||
#include <nlohmann/detail/view/pointer.hpp>
|
||||
#include <nlohmann/detail/view/string_ref.hpp>
|
||||
#include <nlohmann/detail/view/value.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
|
||||
@@ -268,6 +273,14 @@ class basic_json_view
|
||||
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.
|
||||
@@ -315,6 +328,51 @@ class basic_json_view
|
||||
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
|
||||
@@ -381,6 +439,13 @@ class basic_json_view
|
||||
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
|
||||
@@ -438,6 +503,50 @@ class basic_json_view
|
||||
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 //
|
||||
/////////////////
|
||||
@@ -478,6 +587,83 @@ class basic_json_view
|
||||
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 node* m_node = nullptr;
|
||||
};
|
||||
|
||||
@@ -19781,6 +19781,11 @@ NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
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
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/
|
||||
template<typename RefStringType>
|
||||
@@ -19793,6 +19798,8 @@ class json_pointer
|
||||
template<typename>
|
||||
friend class json_pointer;
|
||||
|
||||
friend struct detail::json_pointer_access;
|
||||
|
||||
template<typename T>
|
||||
struct string_t_helper
|
||||
{
|
||||
@@ -20915,6 +20922,20 @@ inline bool operator<(const json_pointer<RefStringTypeLhs>& lhs,
|
||||
}
|
||||
#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
|
||||
|
||||
// #include <nlohmann/detail/json_ref.hpp>
|
||||
|
||||
@@ -27,11 +27,14 @@
|
||||
#include <cstddef> // size_t
|
||||
#include <cstring> // memcpy, strlen
|
||||
#include <iterator> // distance, input_iterator_tag, iterator_traits
|
||||
#include <map> // map
|
||||
#include <memory> // unique_ptr
|
||||
#include <string> // string
|
||||
#include <tuple> // tuple_element, tuple_size
|
||||
#include <type_traits> // enable_if, integral_constant, is_base_of, is_integral, is_same, remove_cv, remove_extent
|
||||
#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 <vector> // vector
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -1617,6 +1620,12 @@ namespace view
|
||||
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
|
||||
|
||||
@@ -2381,6 +2390,181 @@ NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
// #include <nlohmann/detail/view/node.hpp>
|
||||
|
||||
// #include <nlohmann/detail/view/pointer.hpp>
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | 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
|
||||
|
||||
|
||||
|
||||
#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
|
||||
|
||||
// #include <nlohmann/detail/view/string_ref.hpp>
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
@@ -2495,6 +2679,121 @@ class string_ref
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
// #include <nlohmann/detail/view/value.hpp>
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | 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
|
||||
|
||||
|
||||
|
||||
#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
|
||||
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
|
||||
@@ -2713,6 +3012,14 @@ class basic_json_view
|
||||
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.
|
||||
@@ -2760,6 +3067,51 @@ class basic_json_view
|
||||
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
|
||||
@@ -2826,6 +3178,13 @@ class basic_json_view
|
||||
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
|
||||
@@ -2883,6 +3242,50 @@ class basic_json_view
|
||||
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 //
|
||||
/////////////////
|
||||
@@ -2923,6 +3326,83 @@ class basic_json_view
|
||||
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 node* m_node = nullptr;
|
||||
};
|
||||
|
||||
@@ -17,12 +17,18 @@ using nlohmann::ordered_json_document;
|
||||
using nlohmann::ordered_json_view;
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -662,3 +668,386 @@ TEST_CASE("json_view element access and iteration")
|
||||
#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