- Home: add a "Latest release" button - Sponsors: update the list and group it by tier - Features: list all feature pages on the index page - Releases: add highlights per release, link the 4.0 roadmap, add the first commit to the timeline - Exceptions: prefix all example messages with the exception name - API: no code font for format names in the See also links of the to_/from_ pages - Merge Patch: show the document before, the patch, and the result - Replace "--" with typographic dashes, mark external links with an icon, center Mermaid diagrams Signed-off-by: Niels Lohmann <mail@nlohmann.me>
5.7 KiB
JSON Merge Patch
The library supports JSON Merge Patch (RFC 7396) as a patch format. 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. Applying a merge patch changes the JSON value the patch is applied to.
Instead of using JSON Pointer to specify values to be manipulated, it describes the changes using a syntax that closely mimics the document being modified. Unlike JSON Patch, a JSON Merge Patch cannot express every kind of change (e.g., it cannot reorder array elements or remove a specific array element), but it is easier to read and write for object-shaped documents.
The function merge_diff creates a merge patch that changes one JSON value into
another.
The example below applies one patch to one document. Here is the document before:
{
"title": "Goodbye!",
"author": {
"givenName": "John",
"familyName": "Doe"
},
"tags": ["example", "sample"],
"content": "This will be unchanged"
}
This is the patch:
{
"title": "Hello!",
"phoneNumber": "+01-123-456-7890",
"author": {
"familyName": null
},
"tags": ["example"]
}
And this is the document after applying it:
{
"title": "Hello!",
"author": {
"givenName": "John"
},
"tags": ["example"],
"content": "This will be unchanged",
"phoneNumber": "+01-123-456-7890"
}
The table goes through the members one by one. "Member" is the name of an object member in the patch (and in the
document); it is not a JSON Pointer and nothing of it is passed to the function: the whole patch is
passed to merge_patch.
| Rule | Member | Before | Patch | After |
|---|---|---|---|---|
| change value | title |
#!json "Goodbye!" |
#!json "Hello!" |
#!json "Hello!" |
| add key | phoneNumber |
(no member) | #!json "+01-123-456-7890" |
#!json "+01-123-456-7890" |
| object merges recursively | author |
#!json {"givenName": "John", "familyName": "Doe"} |
#!json {"familyName": null} |
#!json {"givenName": "John"} |
| null removes key | familyName inside author |
#!json "Doe" |
#!json null |
(removed) |
| array replaced whole | tags |
#!json ["example", "sample"] |
#!json ["example"] |
#!json ["example"] |
| unchanged key | content |
#!json "This will be unchanged" |
(no member) | #!json "This will be unchanged" |
An array in the patch replaces the array in the target, so a merge patch cannot change a single element. Each member of the patch is applied like this; a patch that is not an object replaces the whole target.
flowchart LR
M["member of the patch"] --> N{{"value null?"}}
N -->|yes| D["delete the key"]
N -->|no| O{{"value an object?"}}
O -->|yes| R["merge recursively into the target's value"]
O -->|no| S["set the key to the value"]
How one member of a merge patch is applied: a `null` value deletes the key, an object is merged recursively, and any other value replaces the value of the key.
!!! tip "Not a general deep merge"
A merge patch is not a general deep merge: a `#!json null` value in the patch deletes the key from the target.
To merge two objects recursively (e.g., defaults and user settings), use
[`update`](../api/basic_json/update.md) with `merge_objects` set to `#!cpp true`; see
[Merging objects](modifying_values.md#merging-objects).
!!! note "Limitations of RFC 7396"
- A patch cannot set a member to `#!json null`, because `#!json null` removes the member:
`#!json {"a":1}` patched with `#!json {"a":null}` gives `#!json {}`.
- Arrays are replaced as a whole, never merged, and a `#!json null` inside an array is kept.
- `#!json null` members of the target that the patch does not mention are preserved.
- A `#!json null` that would remove a member of a not yet existing object still creates that object:
`#!json {}` patched with `#!json {"a":{"b":null}}` gives `#!json {"a":{}}`.
- A patch that is not an object replaces the whole target (a `#!json null` patch makes the target
`#!json null`); an object patch applied to a non-object target starts from an empty object.
- [`merge_diff`](../api/basic_json/merge_diff.md#notes) has a matching limitation for `#!json null` members.
??? example
The following code shows how a JSON Merge Patch is applied to a JSON document.
```cpp
--8<-- "examples/merge_patch.cpp"
```
Output:
```json
--8<-- "examples/merge_patch.output"
```
See also
- JSON Patch and Diff - a more expressive alternative that describes a sequence of operations
- JSON Pointer - the addressing scheme used by JSON Patch
merge_patch- apply a JSON Merge Patch to a valuemerge_diff- create a JSON Merge Patch from two values- Function
update- merge objects, optionally recursively - Modifying values - simple in-place modifications