From fca1ddda96a9cba7087f224ff41903c4e431229f Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Sat, 30 Jul 2022 21:23:55 +0200 Subject: [PATCH] Fix patch::add creating nonexistent parents (#3628) * Fix patch::add creating nonexistent parents The previous behavior was not in accordance with RFC6902. Add unit test. Fixes #3134. * Fix incorrect JSON patch unit test Co-authored-by: Hudson00 --- include/nlohmann/json.hpp | 3 ++- single_include/nlohmann/json.hpp | 3 ++- tests/src/unit-json_patch.cpp | 22 +++++++++++++++------- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 24812fde5..002663686 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -4683,7 +4683,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // get reference to parent of JSON pointer ptr const auto last_path = ptr.back(); ptr.pop_back(); - basic_json& parent = result[ptr]; + // parent must exist when performing patch add per RFC6902 specs + basic_json& parent = result.at(ptr); switch (parent.m_type) { diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 3341f0286..2ddf76c48 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -23527,7 +23527,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // get reference to parent of JSON pointer ptr const auto last_path = ptr.back(); ptr.pop_back(); - basic_json& parent = result[ptr]; + // parent must exist when performing patch add per RFC6902 specs + basic_json& parent = result.at(ptr); switch (parent.m_type) { diff --git a/tests/src/unit-json_patch.cpp b/tests/src/unit-json_patch.cpp index cdb287a9e..20a77714b 100644 --- a/tests/src/unit-json_patch.cpp +++ b/tests/src/unit-json_patch.cpp @@ -32,7 +32,7 @@ TEST_CASE("JSON patch") SECTION("4.1 add") { - json patch = R"([{ "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] }])"_json; + json patch1 = R"([{ "op": "add", "path": "/a/b", "value": [ "foo", "bar" ] }])"_json; // However, the object itself or an array containing it does need // to exist, and it remains an error for that not to be the case. @@ -42,24 +42,32 @@ TEST_CASE("JSON patch") // is not an error, because "a" exists, and "b" will be added to // its value. - CHECK_NOTHROW(doc1.patch(patch)); + CHECK_NOTHROW(doc1.patch(patch1)); auto doc1_ans = R"( { "a": { "foo": 1, - "b": { - "c": [ "foo", "bar" ] - } + "b": [ "foo", "bar" ] } } )"_json; - CHECK(doc1.patch(patch) == doc1_ans); + CHECK(doc1.patch(patch1) == doc1_ans); // It is an error in this document: json doc2 = R"({ "q": { "bar": 2 } })"_json; // because "a" does not exist. - CHECK_THROWS_WITH_AS(doc2.patch(patch), "[json.exception.out_of_range.403] key 'a' not found", json::out_of_range&); + CHECK_THROWS_WITH_AS(doc2.patch(patch1), "[json.exception.out_of_range.403] key 'a' not found", json::out_of_range&); + + json doc3 = R"({ "a": {} })"_json; + json patch2 = R"([{ "op": "add", "path": "/a/b/c", "value": 1 }])"_json; + + // should cause an error because "b" does not exist in doc3 +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH_AS(doc3.patch(patch2), "[json.exception.out_of_range.403] (/a) key 'b' not found", json::out_of_range&); +#else + CHECK_THROWS_WITH_AS(doc3.patch(patch2), "[json.exception.out_of_range.403] key 'b' not found", json::out_of_range&); +#endif } SECTION("4.2 remove")