From 09fb4819ff4b35a6ba5d391f87e4e44f25ad6789 Mon Sep 17 00:00:00 2001 From: Wolf Vollprecht Date: Thu, 21 Jul 2022 16:27:59 +0200 Subject: [PATCH] add patch_inplace function (#3581) * add patch_inplace function to json class * add documentation * fix up docs --- docs/examples/patch_inplace.cpp | 34 ++++++++++ docs/examples/patch_inplace.output | 13 ++++ docs/mkdocs/docs/api/basic_json/patch.md | 1 + .../docs/api/basic_json/patch_inplace.md | 63 +++++++++++++++++++ include/nlohmann/json.hpp | 16 +++-- single_include/nlohmann/json.hpp | 16 +++-- 6 files changed, 131 insertions(+), 12 deletions(-) create mode 100644 docs/examples/patch_inplace.cpp create mode 100644 docs/examples/patch_inplace.output create mode 100644 docs/mkdocs/docs/api/basic_json/patch_inplace.md diff --git a/docs/examples/patch_inplace.cpp b/docs/examples/patch_inplace.cpp new file mode 100644 index 000000000..62bc527d8 --- /dev/null +++ b/docs/examples/patch_inplace.cpp @@ -0,0 +1,34 @@ +#include +#include +#include + +using json = nlohmann::json; + +int main() +{ + // the original document + json doc = R"( + { + "baz": "qux", + "foo": "bar" + } + )"_json; + + // the patch + json patch = R"( + [ + { "op": "replace", "path": "/baz", "value": "boo" }, + { "op": "add", "path": "/hello", "value": ["world"] }, + { "op": "remove", "path": "/foo"} + ] + )"_json; + + // output original document + std::cout << "Before\n" << std::setw(4) << doc << std::endl; + + // apply the patch + doc.patch_inplace(patch); + + // output patched document + std::cout << "After\n" << std::setw(4) << doc << std::endl; +} diff --git a/docs/examples/patch_inplace.output b/docs/examples/patch_inplace.output new file mode 100644 index 000000000..9d31b8ba4 --- /dev/null +++ b/docs/examples/patch_inplace.output @@ -0,0 +1,13 @@ +Before +{ + "baz": "qux", + "foo": "bar" +} + +After +{ + "baz": "boo", + "hello": [ + "world" + ] +} diff --git a/docs/mkdocs/docs/api/basic_json/patch.md b/docs/mkdocs/docs/api/basic_json/patch.md index b3ef963f0..deec47434 100644 --- a/docs/mkdocs/docs/api/basic_json/patch.md +++ b/docs/mkdocs/docs/api/basic_json/patch.md @@ -65,6 +65,7 @@ is thrown. In any case, the original value is not changed: the patch is applied - [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) - [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901) +- [patch_inplace](patch_inplace.md) applies a JSON Patch without creating a copy of the document - [merge_patch](merge_patch.md) applies a JSON Merge Patch ## Version history diff --git a/docs/mkdocs/docs/api/basic_json/patch_inplace.md b/docs/mkdocs/docs/api/basic_json/patch_inplace.md new file mode 100644 index 000000000..0b2055db2 --- /dev/null +++ b/docs/mkdocs/docs/api/basic_json/patch_inplace.md @@ -0,0 +1,63 @@ +# nlohmann::basic_json::patch_inplace + +```cpp +void patch_inplace(const basic_json& json_patch) const; +``` + +[JSON Patch](http://jsonpatch.com) defines a JSON document structure for expressing a sequence of operations to apply to +a JSON document. With this function, a JSON Patch is applied to the current JSON value by executing all operations from the patch. This function applies a JSON patch in place and returns void. + +## Parameters + +`json_patch` (in) +: JSON patch document + +## Exception safety + +No guarantees, value may be corrupted by an unsuccessful patch operation. + +## Exceptions + +- Throws [`parse_error.104`](../../home/exceptions.md#jsonexceptionparse_error104) if the JSON patch does not consist of + an array of objects. +- Throws [`parse_error.105`](../../home/exceptions.md#jsonexceptionparse_error105) if the JSON patch is malformed (e.g., + mandatory attributes are missing); example: `"operation add must have member path"`. +- Throws [`out_of_range.401`](../../home/exceptions.md#jsonexceptionout_of_range401) if an array index is out of range. +- Throws [`out_of_range.403`](../../home/exceptions.md#jsonexceptionout_of_range403) if a JSON pointer inside the patch + could not be resolved successfully in the current JSON value; example: `"key baz not found"`. +- Throws [`out_of_range.405`](../../home/exceptions.md#jsonexceptionout_of_range405) if JSON pointer has no parent + ("add", "remove", "move") +- Throws [`out_of_range.501`](../../home/exceptions.md#jsonexceptionother_error501) if "test" operation was + unsuccessful. + +## Complexity + +Linear in the size of the JSON value and the length of the JSON patch. As usually only a fraction of the JSON value is +affected by the patch, the complexity can usually be neglected. + +## Notes + +Unlike [`patch`](patch.md), `patch_inplace` applies the operation "in place" and no copy of the JSON value is created. That makes it faster for large documents by avoiding the copy. However, the JSON value might be corrupted if the function throws an exception. + +## Examples + +??? example + + The following code shows how a JSON patch is applied to a value. + + ```cpp + --8<-- "examples/patch_inplace.cpp" + ``` + + Output: + + ```json + --8<-- "examples/patch_inplace.output" + ``` + +## See also + +- [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) +- [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901) +- [patch](patch.md) applies a JSON Merge Patch +- [merge_patch](merge_patch.md) applies a JSON Merge Patch diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index faa943618..994ca45dc 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -4620,13 +4620,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @name JSON Patch functions /// @{ - /// @brief applies a JSON patch + /// @brief applies a JSON patch in-place without copying the object /// @sa https://json.nlohmann.me/api/basic_json/patch/ - basic_json patch(const basic_json& json_patch) const + void patch_inplace(const basic_json& json_patch) { - // make a working copy to apply the patch to - basic_json result = *this; - + basic_json& result = *this; // the valid JSON Patch operations enum class patch_operations {add, remove, replace, move, copy, test, invalid}; @@ -4890,7 +4888,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } } } + } + /// @brief applies a JSON patch to a copy of the current object + /// @sa https://json.nlohmann.me/api/basic_json/patch/ + basic_json patch(const basic_json& json_patch) const + { + basic_json result = *this; + result.patch_inplace(json_patch); return result; } @@ -5028,7 +5033,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return result; } - /// @} //////////////////////////////// diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index cc5562d06..3e2b6e285 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -23445,13 +23445,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @name JSON Patch functions /// @{ - /// @brief applies a JSON patch + /// @brief applies a JSON patch in-place without copying the object /// @sa https://json.nlohmann.me/api/basic_json/patch/ - basic_json patch(const basic_json& json_patch) const + void patch_inplace(const basic_json& json_patch) { - // make a working copy to apply the patch to - basic_json result = *this; - + basic_json& result = *this; // the valid JSON Patch operations enum class patch_operations {add, remove, replace, move, copy, test, invalid}; @@ -23715,7 +23713,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } } } + } + /// @brief applies a JSON patch to a copy of the current object + /// @sa https://json.nlohmann.me/api/basic_json/patch/ + basic_json patch(const basic_json& json_patch) const + { + basic_json result = *this; + result.patch_inplace(json_patch); return result; } @@ -23853,7 +23858,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return result; } - /// @} ////////////////////////////////