JSON Patch
JSON Patch is a standard way to write down a change to a JSON document as a list of operations. It is defined in RFC 6902, and a patch is itself plain JSON:
[
{ "op": "replace", "path": "/version", "value": "2.14.0" },
{ "op": "add", "path": "/features/-", "value": "klarna" },
{ "op": "remove", "path": "/owner/oncall" }
]
Applied in order to the original document, those three operations bump the version, append to an array and delete a key. Nothing else in the document is touched.
The six operations
| Operation | What it does | Example |
|---|---|---|
add |
Inserts a value. On an object key it sets it; in an array it inserts at that index | {"op":"add","path":"/tags/0","value":"new"} |
remove |
Deletes the value at the path | {"op":"remove","path":"/owner/oncall"} |
replace |
Overwrites a value that must already exist | {"op":"replace","path":"/replicas","value":6} |
move |
Removes from one path and adds at another | {"op":"move","from":"/draft","path":"/final"} |
copy |
Copies a value to another path | {"op":"copy","from":"/defaults","path":"/staging"} |
test |
Fails the whole patch unless the value equals the one given | {"op":"test","path":"/version","value":"2.13.0"} |
Two details catch people out. In an array, add inserts and shifts
everything after it — it does not overwrite; use replace for that.
And a path ending in /- means "after the last element", which is how
you append without knowing the length.
The patch is atomic: if any operation fails — a replace on a path
that does not exist, a test that does not match — the whole patch is
rejected and the document is left as it was. Putting a test first is
the cheap way to make a patch refuse to apply to a document someone
else has changed in the meantime.
JSON Pointer: the path syntax
Every path and from in a patch is a JSON Pointer
(RFC 6901): slash-separated
steps from the root, with array indexes as plain numbers.
| JSON Pointer | Points at |
|---|---|
"" (empty string) |
The whole document |
/version |
The version key at the top level |
/checks/0/timeout_ms |
timeout_ms of the first item in checks |
/headers/x~1request |
The key x/request — / is written ~1 |
/a~0b |
The key a~b — ~ is written ~0 |
A pointer names exactly one value and has no wildcards or filters.
That is the difference from JSONPath, which is a
query ($.checks[*].name) that can match many values at once. The
same location reads /checks/2/error as a pointer and
$.checks[2].error as JSONPath.
JSON Patch vs JSON Merge Patch
There is a second, simpler format with a confusingly similar name. JSON Merge Patch (RFC 7396) is just a partial document that gets merged in:
{ "version": "2.14.0", "owner": { "oncall": null } }
Keys present overwrite, null deletes, everything else is left alone.
It is easy to write by hand, but it has two blind spots: it cannot set a
value to null (that means delete), and it cannot change one item
inside an array — arrays are replaced whole. JSON Patch has neither
limit, which is why APIs that edit nested lists — Kubernetes
kubectl patch --type=json, many REST PATCH endpoints using the
application/json-patch+json media type — accept it.
Where you meet it
- HTTP
PATCHrequests, withContent-Type: application/json-patch+json. - Kubernetes, for surgical edits to a resource's spec.
- Config and infrastructure review, where the patch is the change someone has to approve.
- Sync and undo: store the patch instead of the whole document, and replay or invert it later.
- Agents editing structured files, where a short list of operations is easier to check than a rewritten file.
Generating a patch from two files
Writing a patch by hand is fine for three operations and error-prone
for thirty. Comma's free JSON diff compares two
documents structurally — key order and formatting ignored, arrays of
records matched by id — and its JSON Patch tab gives you the
RFC 6902 patch that turns the left document into the right one. It
uses add, remove and replace only, addresses arrays by index,
and runs entirely in your browser.
If the change is one a reviewer has to sign off, publish the diff: it becomes a link where they comment on the exact path they are questioning, with the patch attached underneath.
Try it
Comma is free — unlimited reports, unlimited commenters, unlimited revision history.