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 PATCH requests, with Content-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.

Open the JSON diff →

Related