mirror of
https://github.com/nlohmann/json.git
synced 2026-10-10 08:27:13 +00:00
Set a null value below a pointer as basic_json does
set(json_pointer, value) turned a null parent into an object for every
last reference token, so "/a/0" and "/a/-" below {"a":null} made
{"a":{"0":1}}, where basic_json's operator[](json_pointer) makes an
array. A null parent now becomes an array for "-" and for digit tokens
(filled with nulls up to the index) and an object otherwise. An invalid
index is reported before the parent changes.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
3 files changed
+95
-6
No files matched your search
@@ -31,7 +31,10 @@ has `set`; calling it on a read-only `basic_json_document` fails to compile (`#!
|
||||
4. Sets the value the JSON pointer `ptr` refers to, relative to [`root()`](root.md), to `value`. The *parent* of the
|
||||
target must already exist: an object member is set as in 2. (added if it does not exist yet), an array element is
|
||||
assigned as in 3., and a last reference token of `#!cpp "-"`, or equal to the size of the array, appends `value`
|
||||
instead, exactly as [`push_back`](push_back.md) would. An empty `ptr` sets [`root()`](root.md) itself, as in 1.
|
||||
instead, exactly as [`push_back`](push_back.md) would. A [null](../basic_json_view/is_null.md) parent becomes what
|
||||
[`basic_json::operator[]`](../basic_json/operator%5B%5D.md) with a JSON pointer makes of it: an array if the last
|
||||
reference token is `#!cpp "-"` or consists of digits only (for an index beyond 0, the array is first filled with
|
||||
null values up to that index), an object otherwise. An empty `ptr` sets [`root()`](root.md) itself, as in 1.
|
||||
|
||||
In every overload, `value` is accepted three ways: a [`basic_json_view`](../basic_json_view/index.md) of *any*
|
||||
document -- read-only or editable, and it does not have to be `target`'s/`object`'s/`array`'s own document -- which
|
||||
@@ -85,7 +88,8 @@ invalid argument, or `#!cpp std::bad_alloc`) leaves the document completely unch
|
||||
for the encoding that is not reclaimed. A failure of a later allocation -- while an edited array or object switches
|
||||
from its parsed layout to a growable block, see [Notes](#notes) -- can still leave a partial effect, such as a
|
||||
[null](../basic_json_view/is_null.md) `object`/`array` argument already turned into an empty object/array even
|
||||
though `value` itself was not linked in.
|
||||
though `value` itself was not linked in. Likewise, a failure of `value` in 4. leaves a null parent that is set with an
|
||||
index beyond 0 already filled with the null values before the index.
|
||||
|
||||
## Exceptions
|
||||
|
||||
@@ -111,8 +115,10 @@ though `value` itself was not linked in.
|
||||
[`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106) (a leading `#!cpp '0'`),
|
||||
[`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) (not a number),
|
||||
[`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) (too large for `size_type`), or
|
||||
[`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) (an empty token). Also throws what 1.
|
||||
throws for `value`.
|
||||
[`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) (an empty token); the same errors are
|
||||
thrown for a null parent and a token of digits (the parent is not changed then), and
|
||||
[`out_of_range.401`](../../home/exceptions.md#jsonexceptionout_of_range401) if the index is 4294967295 or more.
|
||||
Also throws what 1. throws for `value`.
|
||||
|
||||
Every overload also throws [`type_error.319`](../../home/exceptions.md#jsonexceptiontype_error319) if `value` is (or
|
||||
contains) a binary value -- `BasicJsonType` can hold one, but a `json_document` cannot -- and
|
||||
@@ -131,7 +137,7 @@ document") if `target`/`object`/`array` is a [discarded](../basic_json_view/is_d
|
||||
3. Constant, plus the complexity of 1. for `value`.
|
||||
4. 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 [`at`](../basic_json_view/at.md)), plus the complexity of 2. or 3. for
|
||||
the last token.
|
||||
the last token; for a null parent and an index, linear in the index.
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#ifndef INCLUDE_NLOHMANN_JSON_VIEW_HPP_
|
||||
#define INCLUDE_NLOHMANN_JSON_VIEW_HPP_
|
||||
|
||||
#include <algorithm> // all_of
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // uint32_t
|
||||
#include <cstring> // memcpy, strlen
|
||||
@@ -1040,7 +1041,9 @@ class basic_json_document
|
||||
|
||||
/// set the value at a JSON pointer: its parent must exist; an object
|
||||
/// member is set (added if missing), an array element assigned, and "-"
|
||||
/// or the size of the array appends
|
||||
/// or the size of the array appends. A null parent becomes what
|
||||
/// basic_json's operator[](json_pointer) makes of it: an array for "-"
|
||||
/// and for digits (padded with nulls up to the index), an object otherwise.
|
||||
template<typename V>
|
||||
view_type set(const json_pointer& ptr, V&& value)
|
||||
{
|
||||
@@ -1050,6 +1053,31 @@ class basic_json_document
|
||||
}
|
||||
const view_type parent = root().at(ptr.parent_pointer());
|
||||
const auto& token = ptr.back();
|
||||
if (parent.is_null())
|
||||
{
|
||||
const bool digits = std::all_of(token.begin(), token.end(), [](const char c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
});
|
||||
if (token == "-")
|
||||
{
|
||||
return push_back(parent, std::forward<V>(value));
|
||||
}
|
||||
if (digits)
|
||||
{
|
||||
// (an invalid index is an error before the parent changes)
|
||||
const std::size_t idx = pointer_index(token);
|
||||
if (idx >= 0xFFFFFFFFu)
|
||||
{
|
||||
detail::view::throw_out_of_range(401, detail::concat("array index ", std::to_string(idx), " is out of range"));
|
||||
}
|
||||
for (std::size_t i = 0; i < idx; ++i)
|
||||
{
|
||||
push_back(parent, nullptr);
|
||||
}
|
||||
return push_back(parent, std::forward<V>(value));
|
||||
}
|
||||
}
|
||||
if (parent.is_array())
|
||||
{
|
||||
const std::size_t idx = token == "-" ? parent.size() : pointer_index(token);
|
||||
|
||||
@@ -634,6 +634,61 @@ TEST_CASE("json_view edits: deeply nested values")
|
||||
}
|
||||
|
||||
#if !defined(JSON_NOEXCEPTION)
|
||||
TEST_CASE("json_view edits: pointers below a null value")
|
||||
{
|
||||
// a null value on the way becomes what basic_json makes of it: an array
|
||||
// for "-" and for digits, an object otherwise
|
||||
struct test_case
|
||||
{
|
||||
const char* document;
|
||||
const char* pointer;
|
||||
};
|
||||
const std::array<test_case, 16> cases =
|
||||
{
|
||||
{
|
||||
{R"({"a":null})", "/a/0"},
|
||||
{R"({"a":null})", "/a/-"},
|
||||
{R"({"a":null})", "/a/3"},
|
||||
{R"({"a":null})", "/a/x"},
|
||||
{R"({"a":null})", "/a/+1"},
|
||||
{R"({"a":null})", "/a/01"},
|
||||
{R"({"a":null})", "/a/"},
|
||||
{R"({"a":{"b":null}})", "/a/b/1"},
|
||||
{R"({"a":{"b":null}})", "/a/b/-"},
|
||||
{R"({"a":[null]})", "/a/0/0"},
|
||||
{R"({"a":[null,null]})", "/a/1/k"},
|
||||
{R"([null])", "/0"},
|
||||
{"null", "/0"},
|
||||
{"null", "/-"},
|
||||
{"null", "/k"},
|
||||
{"null", ""},
|
||||
}
|
||||
};
|
||||
for (const test_case& c : cases)
|
||||
{
|
||||
CAPTURE(c.document)
|
||||
CAPTURE(c.pointer)
|
||||
json expected = json::parse(c.document);
|
||||
const std::string error = exception_of_call([&]
|
||||
{
|
||||
expected[json::json_pointer(c.pointer)] = 1;
|
||||
});
|
||||
json_editable_document d = json_editable_document::parse(c.document);
|
||||
if (error.empty())
|
||||
{
|
||||
d.set(json::json_pointer(c.pointer), 1);
|
||||
CHECK(d.root().dump() == expected.dump());
|
||||
CHECK(d.root().materialize() == expected);
|
||||
}
|
||||
else
|
||||
{
|
||||
// the same error, and the document is not changed
|
||||
CHECK(exception_of_call([&] { d.set(json::json_pointer(c.pointer), 1); }) == error);
|
||||
CHECK(d.root().dump() == json::parse(c.document).dump());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("json_view edits: strings of other documents are checked")
|
||||
{
|
||||
// A document borrows the text it was parsed from, and sees later changes
|
||||
|
||||
Reference in new issue
Block a user