Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSON Merge Patch (RFC 7396) #876

Merged
merged 3 commits into from
Jan 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions develop/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7397,6 +7397,7 @@ class basic_json
diff for two JSON values.,diff}

@sa @ref patch -- apply a JSON patch
@sa @ref merge_patch -- apply a JSON Merge Patch

@sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)

Expand Down Expand Up @@ -7528,6 +7529,83 @@ class basic_json
}

/// @}

////////////////////////////////
// JSON Merge Patch functions //
////////////////////////////////

/// @name JSON Merge Patch functions
/// @{

/*!
@brief applies a JSON Merge Patch

The merge patch format is primarily intended for use with the HTTP PATCH
method as a means of describing a set of modifications to a target
resource's content. This function applies a merge patch to the current
JSON value.

The function implements the following algorithm from Section 2 of
[RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396):

```
define MergePatch(Target, Patch):
if Patch is an Object:
if Target is not an Object:
Target = {} // Ignore the contents and set it to an empty Object
for each Name/Value pair in Patch:
if Value is null:
if Name exists in Target:
remove the Name/Value pair from Target
else:
Target[Name] = MergePatch(Target[Name], Value)
return Target
else:
return Patch
```

Thereby, `Target` is the current object; that is, the patch is applied to
the current value.

@param[in] patch the patch to apply

@complexity Linear in the lengths of @a patch.

@liveexample{The following code shows how a JSON Merge Patch is applied to
a JSON document.,merge_patch}

@sa @ref patch -- apply a JSON patch
@sa [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396)

@since version 3.0.0
*/
void merge_patch(const basic_json& patch)
{
if (patch.is_object())
{
if (not is_object())
{
*this = object();
}
for (auto it = patch.begin(); it != patch.end(); ++it)
{
if (it.value().is_null())
{
erase(it.key());
}
else
{
operator[](it.key()).merge_patch(it.value());
}
}
}
else
{
*this = patch;
}
}

/// @}
};

//////////////////
Expand Down
40 changes: 40 additions & 0 deletions doc/examples/merge_patch.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include <iostream>
#include "json.hpp"
#include <iomanip> // for std::setw

using json = nlohmann::json;

int main()
{
// the original document
json document = R"({
"title": "Goodbye!",
"author": {
"givenName": "John",
"familyName": "Doe"
},
"tags": [
"example",
"sample"
],
"content": "This will be unchanged"
})"_json;

// the patch
json patch = R"({
"title": "Hello!",
"phoneNumber": "+01-123-456-7890",
"author": {
"familyName": null
},
"tags": [
"example"
]
})"_json;

// apply the patch
document.merge_patch(patch);

// output original and patched document
std::cout << std::setw(4) << document << std::endl;
}
1 change: 1 addition & 0 deletions doc/examples/merge_patch.link
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<a target="_blank" href="https://wandbox.org/permlink/h13IsbffwmMfqsej"><b>online</b></a>
11 changes: 11 additions & 0 deletions doc/examples/merge_patch.output
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"author": {
"givenName": "John"
},
"content": "This will be unchanged",
"phoneNumber": "+01-123-456-7890",
"tags": [
"example"
],
"title": "Hello!"
}
78 changes: 78 additions & 0 deletions src/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14685,6 +14685,7 @@ class basic_json
diff for two JSON values.,diff}

@sa @ref patch -- apply a JSON patch
@sa @ref merge_patch -- apply a JSON Merge Patch

@sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)

Expand Down Expand Up @@ -14816,6 +14817,83 @@ class basic_json
}

/// @}

////////////////////////////////
// JSON Merge Patch functions //
////////////////////////////////

/// @name JSON Merge Patch functions
/// @{

/*!
@brief applies a JSON Merge Patch

The merge patch format is primarily intended for use with the HTTP PATCH
method as a means of describing a set of modifications to a target
resource's content. This function applies a merge patch to the current
JSON value.

The function implements the following algorithm from Section 2 of
[RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396):

```
define MergePatch(Target, Patch):
if Patch is an Object:
if Target is not an Object:
Target = {} // Ignore the contents and set it to an empty Object
for each Name/Value pair in Patch:
if Value is null:
if Name exists in Target:
remove the Name/Value pair from Target
else:
Target[Name] = MergePatch(Target[Name], Value)
return Target
else:
return Patch
```

Thereby, `Target` is the current object; that is, the patch is applied to
the current value.

@param[in] patch the patch to apply

@complexity Linear in the lengths of @a patch.

@liveexample{The following code shows how a JSON Merge Patch is applied to
a JSON document.,merge_patch}

@sa @ref patch -- apply a JSON patch
@sa [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396)

@since version 3.0.0
*/
void merge_patch(const basic_json& patch)
{
if (patch.is_object())
{
if (not is_object())
{
*this = object();
}
for (auto it = patch.begin(); it != patch.end(); ++it)
{
if (it.value().is_null())
{
erase(it.key());
}
else
{
operator[](it.key()).merge_patch(it.value());
}
}
}
else
{
*this = patch;
}
}

/// @}
};

//////////////////
Expand Down
1 change: 1 addition & 0 deletions test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ SOURCES = src/unit.cpp \
src/unit-iterator_wrapper.cpp \
src/unit-iterators1.cpp \
src/unit-iterators2.cpp \
src/unit-merge_patch.cpp \
src/unit-json_patch.cpp \
src/unit-json_pointer.cpp \
src/unit-meta.cpp \
Expand Down
Loading