diff --git a/src/json.hpp b/src/json.hpp index c1bf6648..8d5291ce 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -9526,6 +9526,44 @@ basic_json_parser_63: throw std::domain_error("JSON patch must be an array of objects"); } + const auto operation_add = [&result](json_pointer & ptr, + basic_json & value) + { + const auto last_path = ptr.pop_back(); + basic_json& parent = result.at(ptr); + + if (parent.is_object()) + { + parent[last_path] = value; + } + else if (parent.is_array()) + { + if (last_path == "-") + { + parent.push_back(value); + } + else + { + parent.insert(parent.begin() + std::stoi(last_path), + value); + } + } + }; + + const auto operation_remove = [&result](json_pointer & ptr) + { + const auto last_path = ptr.pop_back(); + basic_json& parent = result.at(ptr); + if (parent.is_object()) + { + parent.erase(parent.find(last_path)); + } + else if (parent.is_array()) + { + parent.erase(parent.begin() + std::stoi(last_path)); + } + }; + for (const auto& val : patch) { if (not val.is_object()) @@ -9560,38 +9598,11 @@ basic_json_parser_63: throw std::domain_error("'add' operation must have member 'value'"); } - const auto last_path = ptr.pop_back(); - basic_json& parent = result.at(ptr); - - if (parent.is_object()) - { - parent[last_path] = it_value->second; - } - else if (parent.is_array()) - { - if (last_path == "-") - { - parent.push_back(it_value->second); - } - else - { - parent.insert(parent.begin() + std::stoi(last_path), - it_value->second); - } - } + operation_add(ptr, it_value->second); } else if (op == "remove") { - const auto last_path = ptr.pop_back(); - basic_json& parent = result.at(ptr); - if (parent.is_object()) - { - parent.erase(parent.find(last_path)); - } - else if (parent.is_array()) - { - parent.erase(parent.begin() + std::stoi(last_path)); - } + operation_remove(ptr); } else if (op == "replace") { @@ -9610,7 +9621,11 @@ basic_json_parser_63: } const std::string from_path = it_from->second; - const json_pointer from_ptr(from_path); + json_pointer from_ptr(from_path); + basic_json v = result[from_ptr]; + + operation_remove(from_ptr); + operation_add(ptr, v); } else if (op == "copy") { diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index aa02bbea..37feeec6 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -8836,6 +8836,44 @@ class basic_json throw std::domain_error("JSON patch must be an array of objects"); } + const auto operation_add = [&result](json_pointer & ptr, + basic_json & value) + { + const auto last_path = ptr.pop_back(); + basic_json& parent = result.at(ptr); + + if (parent.is_object()) + { + parent[last_path] = value; + } + else if (parent.is_array()) + { + if (last_path == "-") + { + parent.push_back(value); + } + else + { + parent.insert(parent.begin() + std::stoi(last_path), + value); + } + } + }; + + const auto operation_remove = [&result](json_pointer & ptr) + { + const auto last_path = ptr.pop_back(); + basic_json& parent = result.at(ptr); + if (parent.is_object()) + { + parent.erase(parent.find(last_path)); + } + else if (parent.is_array()) + { + parent.erase(parent.begin() + std::stoi(last_path)); + } + }; + for (const auto& val : patch) { if (not val.is_object()) @@ -8870,38 +8908,11 @@ class basic_json throw std::domain_error("'add' operation must have member 'value'"); } - const auto last_path = ptr.pop_back(); - basic_json& parent = result.at(ptr); - - if (parent.is_object()) - { - parent[last_path] = it_value->second; - } - else if (parent.is_array()) - { - if (last_path == "-") - { - parent.push_back(it_value->second); - } - else - { - parent.insert(parent.begin() + std::stoi(last_path), - it_value->second); - } - } + operation_add(ptr, it_value->second); } else if (op == "remove") { - const auto last_path = ptr.pop_back(); - basic_json& parent = result.at(ptr); - if (parent.is_object()) - { - parent.erase(parent.find(last_path)); - } - else if (parent.is_array()) - { - parent.erase(parent.begin() + std::stoi(last_path)); - } + operation_remove(ptr); } else if (op == "replace") { @@ -8920,7 +8931,11 @@ class basic_json } const std::string from_path = it_from->second; - const json_pointer from_ptr(from_path); + json_pointer from_ptr(from_path); + basic_json v = result[from_ptr]; + + operation_remove(from_ptr); + operation_add(ptr, v); } else if (op == "copy") { diff --git a/test/unit.cpp b/test/unit.cpp index 277daed1..907e68bf 100644 --- a/test/unit.cpp +++ b/test/unit.cpp @@ -12521,6 +12521,68 @@ TEST_CASE("JSON patch") CHECK(doc.apply_patch(patch) == expected); } + SECTION("example A.6 - Moving a Value") + { + // An example target JSON document: + json doc = R"( + { + "foo": { + "bar": "baz", + "waldo": "fred" + }, + "qux": { + "corge": "grault" + } + } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "move", "from": "/foo/waldo", "path": "/qux/thud" } + ] + )"_json; + + // The resulting JSON document: + json expected = R"( + { + "foo": { + "bar": "baz" + }, + "qux": { + "corge": "grault", + "thud": "fred" + } + } + )"_json; + + // check if patched value is as expected + CHECK(doc.apply_patch(patch) == expected); + } + + SECTION("example A.7 - Moving a Value") + { + // An example target JSON document: + json doc = R"( + { "foo": [ "all", "grass", "cows", "eat" ] } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "move", "from": "/foo/1", "path": "/foo/3" } + ] + )"_json; + + // The resulting JSON document: + json expected = R"( + { "foo": [ "all", "cows", "eat", "grass" ] } + )"_json; + + // check if patched value is as expected + CHECK(doc.apply_patch(patch) == expected); + } + SECTION("example A.8 - Testing a Value: Success") { // An example target JSON document: