JSON Pointer and JSON Patch
RFC 6901 (JSON Pointer) and RFC 6902 (JSON Patch) give you standardized addressing and diffing for JSON documents, so path-based access and structural diffs don't have to be hand-written traversal code.
JSON Pointer (RFC 6901)
A JSON Pointer is a /-separated path into a document — /a/b/0 means "key a, then key b,
then index 0":
{
"a": {
"b": [10, 20, 30]
}
}
json::json_pointer ptr("/a/b/0");
int v = doc.at(ptr).get<int>(); // 10, using at() with a pointer
int v2 = doc["/a/b/0"_json_pointer]; // same thing, via the literal
Two characters need escaping inside a pointer segment because they're part of the pointer syntax
itself: ~ is written ~0 and / is written ~1 — so a key literally named "a/b" is addressed
as /a~1b.
flatten() and unflatten()
flatten() turns any nested document into a single flat object whose keys are JSON Pointers and
whose values are the corresponding leaf values; unflatten() reverses it:
// before flatten()
{ "a": { "b": [10, 20] } }
// after flatten()
{ "/a/b/0": 10, "/a/b/1": 20 }
This round-trips exactly: doc.flatten().unflatten() == doc.
JSON Patch (RFC 6902)
A JSON Patch is a JSON array of operations describing edits to apply to a document. The six
operations are add, remove, replace, move, copy, and test:
[
{ "op": "replace", "path": "/a/b/0", "value": 99 },
{ "op": "add", "path": "/a/c", "value": "new" },
{ "op": "remove", "path": "/a/b/1" }
]
json patch = json::parse(patch_text);
json patched = doc.patch(patch); // doc itself is unchanged; patched is the result
diff()
json::diff(source, target) produces a JSON Patch that turns source into target:
json patch = json::diff(old_doc, new_doc);
json result = old_doc.patch(patch); // result == new_doc
diff() produces a syntactically minimal patch by comparing document structure, not a
semantically minimal one. Moving an array element from index 0 to index 2, for example, generally
comes out as a sequence of replace operations on the affected indices rather than a single move
— the algorithm doesn't attempt to recognize that the values were merely reordered. Don't rely on
diff() output to convey developer intent, only to reproduce the resulting document.
Failure modes
doc.at(bad_pointer)throwsout_of_rangewhen the path doesn't resolve to an existing element.- Constructing a
json_pointerfrom a malformed string (missing leading/, invalid escape) throwsparse_error. - A
testoperation in a patch whosevaluedoesn't match the document at that path throwsother_errorwhen the patch is applied, aborting the wholepatch()call.
See also
- Merging and comparison —
update()andmerge_patch(), the other ways to combine two documents. - Element access — direct key/index access as an alternative to pointers.
- Error handling and exceptions — the exceptions this page's failure modes throw.
- nlohmann/json overview — the full section map.