mirror of
https://github.com/nlohmann/json.git
synced 2026-10-26 15:58:55 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc95cf5fa8 |
No files matched your search
@@ -61,11 +61,18 @@ Strong exception safety: if an exception occurs, the original value stays intact
|
||||
|
||||
## Complexity
|
||||
|
||||
Logarithmic in the size of the JSON object.
|
||||
1. Logarithmic in the size of the JSON object.
|
||||
2. Logarithmic in the size of the JSON object.
|
||||
3. At most one lookup per reference token of `ptr`, each logarithmic in the size of the container at that level:
|
||||
O(*n* log *m*) for *n* reference tokens and containers of size at most *m*.
|
||||
4. Deleted; a call with an integral argument does not compile.
|
||||
|
||||
## Notes
|
||||
|
||||
- This method always returns `#!cpp false` when executed on a JSON type that is not an object.
|
||||
- Overloads (1) and (2) always return `#!cpp false` when executed on a JSON type that is not an object. Overload (3)
|
||||
also works on arrays and on primitive values: the empty JSON pointer resolves in every JSON value (so
|
||||
`#!cpp json(1).contains(""_json_pointer)` returns `#!cpp true`), and `#!cpp json({1, 2}).contains("/1"_json_pointer)`
|
||||
returns `#!cpp true`.
|
||||
- This method can be executed on any JSON value type.
|
||||
- Calling this function with an integer argument (for example, `#!cpp contains(0)`) does not compile: such an argument
|
||||
would otherwise implicitly convert to a null `#!cpp const char*` and, from there, cause undefined behavior when
|
||||
|
||||
@@ -137,13 +137,10 @@ Strong exception safety: if an exception occurs, the original value stays intact
|
||||
|
||||
When the JSON pointer traverses intermediate levels that don't exist at all yet (not just a missing
|
||||
leaf), each missing level is created as an array or an object depending on whether the corresponding
|
||||
pointer token is a valid array index: the token `0`, a sequence of digits that does not begin with `0`,
|
||||
or the token `-` creates an array, and every other token creates an object. For example, on an
|
||||
initially `#!json null` value, `/foo/0/0/0` creates nested arrays, while `/foo/one/one/one` creates
|
||||
nested objects. Tokens such as `01` or the empty token cannot be array indices (cf. RFC 6901, Sect. 4)
|
||||
and therefore create objects, just as they would if the level already existed as an object. This is not
|
||||
specified by the JSON Pointer RFC; it is this library's own, intentional disambiguation rule. See also
|
||||
[JSON Pointer](../../features/json_pointer.md).
|
||||
pointer token parses as a non-negative integer: a numeric token creates an array, a non-numeric token
|
||||
creates an object. For example, on an initially `#!json null` value, `/foo/0/0/0` creates nested arrays,
|
||||
while `/foo/one/one/one` creates nested objects. This is not specified by the JSON Pointer RFC; it is
|
||||
this library's own, intentional disambiguation rule. See also [JSON Pointer](../../features/json_pointer.md).
|
||||
|
||||
!!! warning "Deprecation"
|
||||
|
||||
|
||||
@@ -36,9 +36,9 @@ The function can throw the following exceptions:
|
||||
- Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if an array index in a key is not a
|
||||
number; example: `"array index 'one' is not a number"`
|
||||
- Throws [`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) if a level becomes an array
|
||||
(because one of its keys is `0`) and another key at that level begins with a digit but is not a valid array index
|
||||
(such as `1a`), or is `-`; example:
|
||||
`"unresolved reference token '-'"`
|
||||
(because one of its keys is `0`) and another key at that level is empty (as in `/a/`), is `-`, or begins with a digit
|
||||
but is not a valid array index (such as `1a`); examples: `"unresolved reference token '-'"`,
|
||||
`"unresolved reference token ''"`
|
||||
|
||||
## Complexity
|
||||
|
||||
@@ -46,7 +46,7 @@ Linear in the size of the JSON value.
|
||||
|
||||
## Notes
|
||||
|
||||
Empty objects and arrays are flattened by [`flatten()`](flatten.md) to `#!json null` values and cannot unflattened to
|
||||
Empty objects and arrays are flattened by [`flatten()`](flatten.md) to `#!json null` values and cannot be unflattened to
|
||||
their original type.
|
||||
|
||||
A flattened array and a flattened object whose keys are array indices are indistinguishable, because both are
|
||||
@@ -54,7 +54,12 @@ described by the same JSON pointers. A value is therefore restored as an array i
|
||||
reference token `0`, and as an object otherwise: `#!json {"2": 1}` is restored unchanged, whereas `#!json {"0": 1}` is
|
||||
restored as `#!json [1]`. This decision does not depend on the order in which the flattened object is iterated.
|
||||
|
||||
Apart from these two cases, for a JSON value `j`, the following is always true:
|
||||
A third case is an object that has the key `0` next to a key that is not a valid array index: `#!json {"0": 1, "b": 2}`
|
||||
is flattened to `#!json {"/0": 1, "/b": 2}`, and unflatten treats the level as an array and throws
|
||||
[`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) because `b` is not a number. (For the keys
|
||||
`-`, `1a`, or the empty key instead of `b`, it throws [`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404).)
|
||||
|
||||
Apart from these three cases, for a JSON value `j`, the following is always true:
|
||||
`#!cpp j == j.flatten().unflatten()`.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -45,6 +45,9 @@ This is equivalent to Python's `dict.get(key, default)`.
|
||||
return default_value;
|
||||
}
|
||||
```
|
||||
with one exception: if the JSON value is neither an array nor an object, the function throws
|
||||
[`type_error.306`](../../home/exceptions.md#jsonexceptiontype_error306) before looking at `ptr`, even for the empty
|
||||
pointer, whereas `at` returns the value itself for the empty pointer.
|
||||
|
||||
!!! note "Differences to `at` and `operator[]`"
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@ Parent of this JSON pointer; in case this JSON pointer is the root, the root its
|
||||
|
||||
## Exception safety
|
||||
|
||||
No-throw guarantee: this function never throws exceptions.
|
||||
Strong exception safety: if an exception occurs, the original value stays intact. The function is not `#!cpp noexcept`
|
||||
and allocates a new JSON pointer, so it can throw `#!cpp std::bad_alloc`; it throws no library exceptions.
|
||||
|
||||
## Complexity
|
||||
|
||||
|
||||
@@ -37,7 +37,8 @@ the stream `o`
|
||||
1. Throws [`type_error.316`](../home/exceptions.md#jsonexceptiontype_error316) if a string stored inside the JSON
|
||||
value is not UTF-8 encoded. Note that unlike the [`dump`](basic_json/dump.md) member functions, no `error_handler`
|
||||
can be set.
|
||||
2. None.
|
||||
2. Throws no library exceptions. As the string representation is built with [`to_string`](json_pointer/to_string.md),
|
||||
`#!cpp std::bad_alloc` is possible.
|
||||
|
||||
## Complexity
|
||||
|
||||
|
||||
@@ -37,6 +37,10 @@ Then every value inside the JSON document can be identified as follows:
|
||||
Note `/` does not identify the root (i.e., the whole document), but an object entry with empty key `""`. See
|
||||
[RFC 6901](https://tools.ietf.org/html/rfc6901) for more information.
|
||||
|
||||
[RFC 6901, section 4](https://tools.ietf.org/html/rfc6901#section-4) states that evaluation fails if an object has
|
||||
non-unique member names. A `basic_json` object holds one value per key (see [`object_t`](../api/basic_json/object_t.md)
|
||||
for how duplicate keys are handled while parsing), so evaluating a JSON Pointer never encounters duplicates.
|
||||
|
||||
## JSON Pointer creation
|
||||
|
||||
JSON Pointers can be created from a string:
|
||||
@@ -54,6 +58,13 @@ auto p = "/nested/one"_json_pointer;
|
||||
The escaping rules of [RFC 6901](https://tools.ietf.org/html/rfc6901) are implemented. See the
|
||||
[constructor documentation](../api/json_pointer/json_pointer.md) for more information.
|
||||
|
||||
!!! note "URI fragment representation"
|
||||
|
||||
[RFC 6901, section 6](https://tools.ietf.org/html/rfc6901#section-6) also defines a URI fragment identifier
|
||||
representation (`#/a%20b`). It is not supported: `#!cpp json::json_pointer("#/a")` throws
|
||||
[`parse_error.107`](../home/exceptions.md#jsonexceptionparse_error107). Strip the leading `#` and
|
||||
percent-decode the remainder before creating the JSON pointer.
|
||||
|
||||
## Value access
|
||||
|
||||
JSON Pointers can be used in the [`at`](../api/basic_json/at.md), [`operator[]`](../api/basic_json/operator%5B%5D.md),
|
||||
|
||||
@@ -390,6 +390,7 @@ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
|
||||
described in RFC 6901. Neither `find(const StringType&, size_type)`, nor `substr(pos, count)`, nor
|
||||
`replace(pos, count, const StringType&)` is required.
|
||||
- `empty()`.
|
||||
- `resize(n)` (one argument) -- used by `to_string` to size the result before the reference tokens are copied into it.
|
||||
- `begin()` and `end()` -- used by
|
||||
[`operator[](const json_pointer&)`](../../api/basic_json/operator%5B%5D.md) to decide whether a reference token
|
||||
denotes an array index.
|
||||
|
||||
@@ -1083,9 +1083,14 @@ a JSON pointer exceeds the range of `size_type` (e.g., on 32-bit platforms).
|
||||
|
||||
```
|
||||
enum value out of range
|
||||
array index 18446744073709551616 exceeds size_type
|
||||
array index 18446744073709551615 exceeds size_type
|
||||
```
|
||||
|
||||
The second message is thrown, for instance, by `#!cpp json::parse("[1, 2]").at("/18446744073709551615"_json_pointer)`
|
||||
on a 64-bit platform, where `size_type` is `#!cpp std::size_t` and this index is `SIZE_MAX`. An index that does not
|
||||
fit into 64 bits (such as `18446744073709551616`) is not reported as 410 but as
|
||||
[`out_of_range.404`](#jsonexceptionout_of_range404).
|
||||
|
||||
### json.exception.out_of_range.411
|
||||
|
||||
A JSON Patch `add` operation cannot be applied because the target location's parent is neither an object nor an array. Per [RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902), an `add` target must reference a member of an existing object or an element of an existing array; a primitive value (string, number, boolean, etc.) cannot receive a new member or element.
|
||||
|
||||
@@ -984,19 +984,15 @@ class json_pointer
|
||||
// convert null values to arrays or objects before continuing
|
||||
if (ptr->is_null())
|
||||
{
|
||||
// check if the reference token is a valid array index, that is
|
||||
// a nonempty sequence of digits without a leading '0'
|
||||
// (cf. RFC 6901, Sect. 4); tokens that could never be a valid
|
||||
// array index (such as "01" or "") are treated as object keys
|
||||
const bool nums = !reference_token.empty()
|
||||
&& (reference_token.size() == 1 || reference_token[0] != '0')
|
||||
&& std::all_of(reference_token.begin(), reference_token.end(),
|
||||
[](const unsigned char x)
|
||||
// check if the reference token is a number
|
||||
const bool nums =
|
||||
std::all_of(reference_token.begin(), reference_token.end(),
|
||||
[](const unsigned char x)
|
||||
{
|
||||
return std::isdigit(x);
|
||||
});
|
||||
|
||||
// change value to an array for array indices or "-" or to object otherwise
|
||||
// change value to an array for numbers or "-" or to object otherwise
|
||||
*ptr = (nums || reference_token == "-")
|
||||
? detail::value_t::array
|
||||
: detail::value_t::object;
|
||||
|
||||
Generated
+5
-9
@@ -24569,19 +24569,15 @@ class json_pointer
|
||||
// convert null values to arrays or objects before continuing
|
||||
if (ptr->is_null())
|
||||
{
|
||||
// check if the reference token is a valid array index, that is
|
||||
// a nonempty sequence of digits without a leading '0'
|
||||
// (cf. RFC 6901, Sect. 4); tokens that could never be a valid
|
||||
// array index (such as "01" or "") are treated as object keys
|
||||
const bool nums = !reference_token.empty()
|
||||
&& (reference_token.size() == 1 || reference_token[0] != '0')
|
||||
&& std::all_of(reference_token.begin(), reference_token.end(),
|
||||
[](const unsigned char x)
|
||||
// check if the reference token is a number
|
||||
const bool nums =
|
||||
std::all_of(reference_token.begin(), reference_token.end(),
|
||||
[](const unsigned char x)
|
||||
{
|
||||
return std::isdigit(x);
|
||||
});
|
||||
|
||||
// change value to an array for array indices or "-" or to object otherwise
|
||||
// change value to an array for numbers or "-" or to object otherwise
|
||||
*ptr = (nums || reference_token == "-")
|
||||
? detail::value_t::array
|
||||
: detail::value_t::object;
|
||||
|
||||
@@ -493,72 +493,6 @@ TEST_CASE("JSON pointers")
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("creating intermediate levels")
|
||||
{
|
||||
SECTION("tokens that are valid array indices create arrays")
|
||||
{
|
||||
json j;
|
||||
j["/0"_json_pointer] = 1;
|
||||
CHECK(j == json({1}));
|
||||
|
||||
json j2;
|
||||
j2["/2"_json_pointer] = 1;
|
||||
CHECK(j2 == json({nullptr, nullptr, 1}));
|
||||
|
||||
json j3;
|
||||
j3["/-"_json_pointer] = 1;
|
||||
CHECK(j3 == json({1}));
|
||||
|
||||
json j4;
|
||||
j4["/foo/0/0"_json_pointer] = 1;
|
||||
CHECK(j4 == json({{"foo", {{1}}}}));
|
||||
}
|
||||
|
||||
SECTION("tokens that are no valid array indices create objects")
|
||||
{
|
||||
json j;
|
||||
j["/one"_json_pointer] = 1;
|
||||
CHECK(j == json({{"one", 1}}));
|
||||
|
||||
// leading '0' can never be a valid array index (RFC 6901, Sect. 4)
|
||||
json j2;
|
||||
j2["/01"_json_pointer] = 1;
|
||||
CHECK(j2 == json({{"01", 1}}));
|
||||
|
||||
// the empty token is a valid object key, but no valid array index
|
||||
json j3;
|
||||
j3["/"_json_pointer] = 1;
|
||||
CHECK(j3 == json({{"", 1}}));
|
||||
}
|
||||
|
||||
SECTION("creating a level yields the same result as reusing it (#5357)")
|
||||
{
|
||||
json j;
|
||||
j["/a/b/01/d"_json_pointer] = "value";
|
||||
|
||||
json j_init = json::object();
|
||||
j_init["/a/b"_json_pointer] = json::object();
|
||||
j_init["/a/b/01/d"_json_pointer] = "value";
|
||||
|
||||
const json expected = json::parse(R"({"a":{"b":{"01":{"d":"value"}}}})");
|
||||
CHECK(j == expected);
|
||||
CHECK(j_init == expected);
|
||||
|
||||
// unflatten uses the same key
|
||||
const json flat = {{"/a/b/01/d", "value"}};
|
||||
CHECK(flat.unflatten() == expected);
|
||||
}
|
||||
|
||||
SECTION("existing arrays still reject invalid indices")
|
||||
{
|
||||
json j = {1, 2, 3};
|
||||
CHECK_THROWS_WITH_AS(j["/01"_json_pointer],
|
||||
"[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'", json::parse_error&);
|
||||
CHECK_THROWS_WITH_AS(j.at("/01"_json_pointer),
|
||||
"[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'", json::parse_error&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("flatten")
|
||||
{
|
||||
json j =
|
||||
@@ -1110,6 +1044,56 @@ TEST_CASE("flatten of structured values")
|
||||
CHECK(flat_object.unflatten() == nlohmann::ordered_json::parse(R"({"b": {"1": 2}})"));
|
||||
}
|
||||
|
||||
#if !defined(JSON_NOEXCEPTION)
|
||||
SECTION("an object with the key \"0\" next to a non-index key does not round-trip")
|
||||
{
|
||||
// the third documented case in which j != j.flatten().unflatten()
|
||||
const json value = {{"a", {{"0", 1}, {"b", 2}}}};
|
||||
const json flat = value.flatten();
|
||||
CHECK(flat == json({{"/a/0", 1}, {"/a/b", 2}}));
|
||||
CHECK_THROWS_WITH_AS(flat.unflatten(),
|
||||
"[json.exception.parse_error.109] parse error: array index 'b' is not a number", json::parse_error&);
|
||||
|
||||
// the same for other keys that are not valid array indices
|
||||
CHECK_THROWS_WITH_AS(json({{"/a/0", 1}, {"/a/1a", 2}}).unflatten(),
|
||||
"[json.exception.out_of_range.404] unresolved reference token '1a'", json::out_of_range&);
|
||||
CHECK_THROWS_WITH_AS(json({{"/a/0", 1}, {"/a/-", 2}}).unflatten(),
|
||||
"[json.exception.out_of_range.404] unresolved reference token '-'", json::out_of_range&);
|
||||
}
|
||||
|
||||
SECTION("unflatten with the empty token among array indices")
|
||||
{
|
||||
CHECK_THROWS_WITH_AS(json({{"/a/0", 1}, {"/a/", 2}}).unflatten(),
|
||||
"[json.exception.out_of_range.404] unresolved reference token ''", json::out_of_range&);
|
||||
CHECK_THROWS_WITH_AS(json({{"/a/0", 1}, {"/a/1", 2}, {"/a/", 3}}).unflatten(),
|
||||
"[json.exception.out_of_range.404] unresolved reference token ''", json::out_of_range&);
|
||||
|
||||
// without an index "0", the empty token is an ordinary object key
|
||||
CHECK(json({{"/a/", 2}}).unflatten() == json({{"a", {{"", 2}}}}));
|
||||
}
|
||||
|
||||
SECTION("value() with the empty pointer on a primitive root")
|
||||
{
|
||||
// unlike at(), value() rejects primitive values before it looks at the pointer
|
||||
const json number = 1;
|
||||
CHECK(number.at(""_json_pointer) == 1);
|
||||
CHECK_THROWS_WITH_AS(number.value(""_json_pointer, 7),
|
||||
"[json.exception.type_error.306] cannot use value() with number", json::type_error&);
|
||||
|
||||
const json string = "s";
|
||||
CHECK_THROWS_WITH_AS(string.value(""_json_pointer, 7),
|
||||
"[json.exception.type_error.306] cannot use value() with string", json::type_error&);
|
||||
|
||||
// contains() accepts the empty pointer on every value
|
||||
CHECK(number.contains(""_json_pointer));
|
||||
CHECK_FALSE(number.contains("/a"_json_pointer));
|
||||
|
||||
// arrays and objects return the default for a missing location
|
||||
CHECK(json::array().value(""_json_pointer, json(7)) == json::array());
|
||||
CHECK(json::array().value("/0"_json_pointer, 7) == 7);
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("objects and arrays interleaved")
|
||||
{
|
||||
const json value =
|
||||
|
||||
Reference in new issue
Block a user