docs: state merge_patch complexity and RFC 7396 limitations; test non-object, discarded, binary cases (#5938)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann authored and GitHub committed 2026-10-26 17:11:55 +01:00
1 parent c20b6eddf4
commit 7a10bf25d2
3 files changed
+120 -5

No files matched your search

+24 -4
View File
@@ -39,13 +39,33 @@ Basic guarantee: if an exception is thrown during the operation, the JSON value
## Complexity
Linear in the lengths of `apply_patch`.
Linear in the total number of values (including nested ones) in `apply_patch`, times the cost of a key-based object
operation (looking up, adding, or erasing a member): logarithmic for [`json`](../json.md), and linear for
[`ordered_json`](../ordered_json.md) (see [`ordered_map` complexity](../ordered_map.md#complexity)). For
`ordered_json`, patching an object that has `n` members with a patch that has `m` members therefore takes O(n·m). The
cost also includes one copy of `apply_patch` (see the notes below).
## Notes
`apply_patch` may be `#!cpp *this` itself or refer to a value contained in `#!cpp *this` (for example, a subobject
returned by `#!cpp (*this)[key]`); it is read as it was when `merge_patch()` was called, before any modification of
`#!cpp *this`.
- `apply_patch` may be `#!cpp *this` itself or refer to a value contained in `#!cpp *this` (for example, a subobject
returned by `#!cpp (*this)[key]`); it is read as it was when `merge_patch()` was called, before any modification of
`#!cpp *this`. To make this possible, the function copies `apply_patch` before it modifies `#!cpp *this`, so the cost
includes one copy of the patch.
- A merge patch uses `#!json null` to remove a member, so it cannot set a member to `#!json null`: applying
`#!json {"a":null}` to `#!json {"a":1}` gives `#!json {}`.
- Arrays are not merged: an array in the patch replaces the target value as a whole, and a `#!json null` inside an
array is kept as an element. `#!json null` members of the target that the patch does not mention are preserved.
- A `#!json null` in the patch only removes a member; it does not prevent the creation of the enclosing objects.
Applying `#!json {"a":{"b":null}}` to `#!json {}` gives `#!json {"a":{}}` (see example 15 in Appendix A of the RFC).
- If `apply_patch` is not an object, it replaces the target as a whole. In particular, a `#!json null` patch turns the
target into `#!json null`. If `apply_patch` is an object and the target is not, the target is first replaced by an
empty object, so applying `#!json {}` to `#!json 1` gives `#!json {}`.
- A merge patch cannot express every change; for example, it cannot reorder array elements or remove a specific array
element. Use [JSON Patch](../../features/json_patch.md) ([RFC 6902](https://tools.ietf.org/html/rfc6902)) for that.
[`merge_diff`](merge_diff.md) has a matching limitation, see its notes.
- A [discarded](is_discarded.md) value is treated like any other non-object value: a discarded
`apply_patch` turns the target into a discarded value, and an object patch replaces a discarded target by an object
as described above.
## Examples
+13 -1
View File
@@ -2,7 +2,7 @@
The library supports JSON Merge Patch ([RFC 7396](https://tools.ietf.org/html/rfc7396)) 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. This function applies a merge patch to the current JSON value.
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](json_pointer.md) to specify values to be manipulated, it describes the changes using a
syntax that closely mimics the document being modified. Unlike [JSON Patch](json_patch.md), a JSON Merge Patch cannot
@@ -19,6 +19,18 @@ another.
[`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.