mirror of
https://github.com/nlohmann/json.git
synced 2026-09-30 03:30:31 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57890cebad |
@@ -678,19 +678,29 @@ class json_pointer
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// may throw parse_error.106/109 for a malformed index; an
|
||||
// index that is syntactically valid but cannot be
|
||||
// represented (out_of_range.404/410) is treated like an
|
||||
// out-of-range index below
|
||||
typename BasicJsonType::size_type idx{};
|
||||
JSON_TRY
|
||||
// tokens that array_index() rejects with parse_error.106/109
|
||||
// are passed on to it; all other tokens that it would reject
|
||||
// with out_of_range.404/410 are detected here, so that this
|
||||
// also works without exceptions
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 && !(reference_token[0] >= '1' && reference_token[0] <= '9')))
|
||||
{
|
||||
idx = array_index<BasicJsonType>(reference_token);
|
||||
static_cast<void>(array_index<BasicJsonType>(reference_token)); // throws parse_error.106/109
|
||||
}
|
||||
JSON_INTERNAL_CATCH (detail::out_of_range&)
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_token.empty() || !std::all_of(reference_token.begin(), reference_token.end(), [](const char c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
})))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
errno = 0; // strtoull() does not reset errno on success
|
||||
char* p_end = nullptr; // NOLINT(misc-const-correctness)
|
||||
const unsigned long long magnitude = std::strtoull(reference_token.data(), &p_end, 10); // NOLINT(runtime/int)
|
||||
if (JSON_HEDLEY_UNLIKELY(errno == ERANGE || magnitude >= static_cast<unsigned long long>((std::numeric_limits<typename BasicJsonType::size_type>::max)()))) // NOLINT(runtime/int)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
const auto idx = static_cast<typename BasicJsonType::size_type>(magnitude);
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
|
||||
{
|
||||
|
||||
@@ -6187,14 +6187,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
}
|
||||
}
|
||||
|
||||
// Only an object type that keeps its members in insertion
|
||||
// order, such as nlohmann::ordered_map, can need reordering:
|
||||
// patch() appends a new member at the end of such an object.
|
||||
// Any other object type places its members itself - std::map
|
||||
// in key order, a hash map in an order its operator== ignores -
|
||||
// so a member-by-member diff always reproduces target there.
|
||||
if (!detail::is_ordered_map<object_t>::value
|
||||
|| (common_keys_source_order == common_keys_target_order && new_keys_form_suffix))
|
||||
if (common_keys_source_order == common_keys_target_order && new_keys_form_suffix)
|
||||
{
|
||||
// fast path: order of common keys already matches (or the
|
||||
// object_t's iteration order does not depend on
|
||||
|
||||
@@ -19520,19 +19520,29 @@ class json_pointer
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// may throw parse_error.106/109 for a malformed index; an
|
||||
// index that is syntactically valid but cannot be
|
||||
// represented (out_of_range.404/410) is treated like an
|
||||
// out-of-range index below
|
||||
typename BasicJsonType::size_type idx{};
|
||||
JSON_TRY
|
||||
// tokens that array_index() rejects with parse_error.106/109
|
||||
// are passed on to it; all other tokens that it would reject
|
||||
// with out_of_range.404/410 are detected here, so that this
|
||||
// also works without exceptions
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 && !(reference_token[0] >= '1' && reference_token[0] <= '9')))
|
||||
{
|
||||
idx = array_index<BasicJsonType>(reference_token);
|
||||
static_cast<void>(array_index<BasicJsonType>(reference_token)); // throws parse_error.106/109
|
||||
}
|
||||
JSON_INTERNAL_CATCH (detail::out_of_range&)
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_token.empty() || !std::all_of(reference_token.begin(), reference_token.end(), [](const char c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
})))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
errno = 0; // strtoull() does not reset errno on success
|
||||
char* p_end = nullptr; // NOLINT(misc-const-correctness)
|
||||
const unsigned long long magnitude = std::strtoull(reference_token.data(), &p_end, 10); // NOLINT(runtime/int)
|
||||
if (JSON_HEDLEY_UNLIKELY(errno == ERANGE || magnitude >= static_cast<unsigned long long>((std::numeric_limits<typename BasicJsonType::size_type>::max)()))) // NOLINT(runtime/int)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
const auto idx = static_cast<typename BasicJsonType::size_type>(magnitude);
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
|
||||
{
|
||||
@@ -32268,14 +32278,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
}
|
||||
}
|
||||
|
||||
// Only an object type that keeps its members in insertion
|
||||
// order, such as nlohmann::ordered_map, can need reordering:
|
||||
// patch() appends a new member at the end of such an object.
|
||||
// Any other object type places its members itself - std::map
|
||||
// in key order, a hash map in an order its operator== ignores -
|
||||
// so a member-by-member diff always reproduces target there.
|
||||
if (!detail::is_ordered_map<object_t>::value
|
||||
|| (common_keys_source_order == common_keys_target_order && new_keys_form_suffix))
|
||||
if (common_keys_source_order == common_keys_target_order && new_keys_form_suffix)
|
||||
{
|
||||
// fast path: order of common keys already matches (or the
|
||||
// object_t's iteration order does not depend on
|
||||
|
||||
@@ -46,6 +46,21 @@ TEST_CASE("Tests with disabled exceptions")
|
||||
CHECK(*sax_no_exception::error_string == "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - invalid literal; last read: 'x'");
|
||||
delete sax_no_exception::error_string; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
}
|
||||
|
||||
SECTION("issue #5672 - value(json_pointer, default) must not abort for array tokens that are not a valid index")
|
||||
{
|
||||
const json j = {1, 2, 3};
|
||||
|
||||
// a syntactically valid index that is out of range for this array
|
||||
CHECK(j.value("/7"_json_pointer, 42) == 42);
|
||||
// a reference token that is not a number at all
|
||||
CHECK(j.value("/1a"_json_pointer, 42) == 42);
|
||||
// the empty reference token (JSON pointer "/")
|
||||
CHECK(j.value("/"_json_pointer, 42) == 42);
|
||||
// an index whose magnitude does not fit into size_type
|
||||
CHECK(j.value("/99999999999999999999999"_json_pointer, 42) == 42);
|
||||
CHECK(j.value("/18446744073709551615"_json_pointer, 42) == 42);
|
||||
}
|
||||
}
|
||||
|
||||
DOCTEST_GCC_SUPPRESS_WARNING_POP
|
||||
|
||||
@@ -482,6 +482,21 @@ TEST_CASE_TEMPLATE("element access 2", Json, nlohmann::json, nlohmann::ordered_j
|
||||
CHECK(j_array.value("/-"_json_pointer, 42) == 42);
|
||||
CHECK(j_array_const.value("/-"_json_pointer, 42) == 42);
|
||||
|
||||
// Test an index with a non-digit after a valid leading digit; this is
|
||||
// out_of_range (not parse_error) and must not throw (see #5672)
|
||||
CHECK(j_array.value("/1a"_json_pointer, 42) == 42);
|
||||
CHECK(j_array_const.value("/1a"_json_pointer, 42) == 42);
|
||||
|
||||
// Test the empty reference token (JSON pointer "/"); see #5672
|
||||
CHECK(j_array.value("/"_json_pointer, 42) == 42);
|
||||
CHECK(j_array_const.value("/"_json_pointer, 42) == 42);
|
||||
|
||||
// Test an index whose magnitude does not fit into size_type (see #5672)
|
||||
CHECK(j_array.value("/99999999999999999999999"_json_pointer, 42) == 42);
|
||||
CHECK(j_array_const.value("/99999999999999999999999"_json_pointer, 42) == 42);
|
||||
CHECK(j_array.value("/18446744073709551615"_json_pointer, 42) == 42);
|
||||
CHECK(j_array_const.value("/18446744073709551615"_json_pointer, 42) == 42);
|
||||
|
||||
#if !defined(JSON_NOEXCEPTION)
|
||||
// Test malformed index (non-numeric) throws parse_error
|
||||
CHECK_THROWS_WITH_AS(j_array.value("/foo"_json_pointer, 1), "[json.exception.parse_error.109] parse error: array index 'foo' is not a number", typename Json::parse_error&);
|
||||
|
||||
@@ -1752,58 +1752,6 @@ TEST_CASE("JSON patch - diff emits array removals in descending index order")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JSON patch - diff() takes the fast path for non-reorderable object types (regression #5639)")
|
||||
{
|
||||
// #5465 added an order check to diff()'s object handling so a
|
||||
// member-by-member diff is only used when it would also reproduce
|
||||
// target's member *order* -- needed for ordered_json, whose object_t
|
||||
// keeps insertion order and whose patch() "add" op appends a new
|
||||
// member at the end. For json's default object_t (std::map, which
|
||||
// orders members by key regardless of insertion history), that check
|
||||
// could still fail: a new key that sorts before an existing common key
|
||||
// makes target's iteration interleave the new key between common keys,
|
||||
// even though nothing else about the object changed. That sent the
|
||||
// whole object through the slow (remove-every-member,
|
||||
// re-add-every-member) path instead of the minimal one.
|
||||
SECTION("json: added key sorts before an existing common key")
|
||||
{
|
||||
const json source = {{"a", 1}, {"c", {{"x", 1}, {"y", 2}}}};
|
||||
const json target = {{"a", 1}, {"b", 0}, {"c", {{"x", 1}, {"y", 2}}}};
|
||||
|
||||
const json patch = json::diff(source, target);
|
||||
|
||||
// only the new key is added; "a" and "c" are left alone instead of
|
||||
// being removed and re-added
|
||||
const json expected = R"([{"op": "add", "path": "/b", "value": 0}])"_json;
|
||||
CHECK(patch == expected);
|
||||
CHECK(source.patch(patch) == target);
|
||||
}
|
||||
|
||||
SECTION("ordered_json: reordering behavior from #5465 is unchanged")
|
||||
{
|
||||
using nlohmann::ordered_json;
|
||||
|
||||
// same key/value shape as the json case above, but for ordered_json
|
||||
// the *target*'s member order must be reproduced, so the slow path
|
||||
// is still required here.
|
||||
ordered_json source;
|
||||
source["a"] = 1;
|
||||
source["c"] = ordered_json{{"x", 1}, {"y", 2}};
|
||||
|
||||
ordered_json target;
|
||||
target["a"] = 1;
|
||||
target["b"] = 0;
|
||||
target["c"] = ordered_json{{"x", 1}, {"y", 2}};
|
||||
|
||||
const ordered_json patch = ordered_json::diff(source, target);
|
||||
|
||||
// unlike the json case: every member is still removed and re-added
|
||||
// so the result ends up in target's order (2 removes + 3 adds)
|
||||
CHECK(patch.size() == 5);
|
||||
CHECK(source.patch(patch) == target);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JSON patch - every operation on ordered_json")
|
||||
{
|
||||
using nlohmann::ordered_json;
|
||||
|
||||
Reference in New Issue
Block a user