* Throw out_of_range.411 for JSON Patch add into a null parent (#5917) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MKJf48vnGtTE25KscmjfaW Signed-off-by: Niels Lohmann <niels.lohmann@gmail.com> * docs: make the diff() and patch() documentation accurate; add tests Document the exceptions to the diff round-trip invariant (NaN, discarded values, number types), the path parameter, non-minimal patches, array and object operation order, the ordered_json fallback, binary values in serialized patches, the real complexity, duplicate member names in operations, test on missing paths, and the exact out_of_range.403 message. Add tests for these behaviors. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <niels.lohmann@gmail.com> Signed-off-by: Niels Lohmann <mail@nlohmann.me> Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
4.7 KiB
nlohmann::basic_json::diff
static basic_json diff(const basic_json& source,
const basic_json& target,
const string_t& path = "");
Creates a JSON Patch so that value source can be changed into the value target by calling
patch function.
For two JSON values source and target, the following code yields #!cpp true:
source.patch(diff(source, target)) == target;
The equality holds up to operator==, so it has the following exceptions:
- A value that is not equal to itself cannot round-trip. This applies to
NaN(also nested in an array or object) and to discarded values:diffemits areplaceoperation for them. ANaNin the patch is serialized as#!json null, so a patch that was dumped and parsed again would set#!json null. - Numbers that compare equal produce no operation, even if their types or signs differ:
diff(1, 1.0),diff(1u, 1)(unsigned and signed integer) anddiff(0.0, -0.0)all return#!json []. The patched value is then thesourceand keeps its number type.
Parameters
source(in)- JSON value to compare from
target(in)- JSON value to compare against
path(in)- a prefix that is put verbatim in front of the
pathmember of every operation (default:"", which addresses the root of the value). The prefix is neither escaped nor validated:#!cpp diff(1, 2, "abc")yields an operation with thepathabc, which is not a valid JSON Pointer. Use it with a valid JSON Pointer such as/rootand escape~and/in its reference tokens (see JSON Pointer).
Return value
a JSON patch to convert the source to target
Exception safety
Strong guarantee: source and target are never modified.
Complexity
Linear in the total number of values (including nested ones) in source and target, times the cost of a key-based
object operation (looking up a key in an object): logarithmic for json, and linear for
ordered_json (see ordered_map complexity). For
ordered_json, diffing two objects with n and m members therefore takes O((n+m)²) when their keys do not line up
(e.g., a key was added or the keys are in a different order). The size of the patch is proportional to the number of
operations times the length of their paths, so a small change deep inside a value yields a long path.
Notes
- Currently, only
remove,add, andreplaceoperations are generated. - The patch is correct, but not guaranteed to be minimal. For instance,
diff([1,2,3], [0,1,2,3])yields threereplaceoperations and oneaddoperation instead of a single insertion. - Differences between two arrays are reported index by index. Elements that were added at the end of the array are added
with
addoperations on the path/-(appended in order), and elements that were removed are removed withremoveoperations from the highest index down to the lowest, so that the remaining indices stay valid. - For objects, the operations for the members of
sourcecome first (replace,remove, or the operations for nested values, in the iteration order ofsource), followed byaddoperations for the members that onlytargethas. - For
ordered_json, objects with the same keys in a different order are different. As a patch cannot reorder members,diffremoves all keys of thesourceobject and adds them again in the order of thetarget. - If
sourceandtargetdiffer as a whole (e.g., different types), the patch consists of a singlereplaceoperation for the root of the value, i.e., with thepath""(or the givenpathprefix). - The
valuemembers of the patch hold copies of values oftarget. A binary value has no JSON representation: when a patch that contains one is serialized, it becomes an object such as#!json {"bytes":[1,3],"subtype":null}, so a parsed patch no longer holds a binary value (and would set that object instead).
Examples
??? example
The following code shows how a JSON patch is created as a diff for two JSON values.
```cpp
--8<-- "examples/diff.cpp"
```
Output:
```json
--8<-- "examples/diff.output"
```
See also
- RFC 6902 (JSON Patch)
- patch applies a JSON Patch
- patch_inplace applies a JSON Patch in place
- merge_patch applies a JSON Merge Patch
- merge_diff creates a diff as a JSON Merge Patch
Version history
- Added in version 2.0.0.