JSON Patch, Merge Patch, JSON Pointer, and JSON Path: The Standards Most Developers Don't Know
JSON Patch (RFC 6902) sends a list of operations (add, remove, replace, test) to modify a resource atomically. JSON Merge Patch (RFC 7396) uses a simpler merge rule. Here's both standards with examples, JSON Pointer path syntax, JSON Path for querying, and why the `test` operation enables optimistic concurrency.
By sadiqbd Β· June 14, 2026
JSON Patch lets you send only what changed β and most REST APIs don't support it yet, even though they should
The standard REST pattern for updating a resource is PUT (replace the entire resource) or PATCH (partial update). Most PATCH implementations accept a partial JSON body and merge it informally β there's no standard for how the merge works. JSON Patch (RFC 6902) and JSON Merge Patch (RFC 7396) are the two standardised approaches, and knowing them changes how you design APIs that handle partial updates cleanly.
The problem with informal PATCH
When a REST API documents PATCH /users/{id}, the typical implementation accepts whatever fields you send and updates them:
PATCH /users/usr_4821
{
"email": "newemail@example.com"
}
This updates the email. But what does this mean?
PATCH /users/usr_4821
{
"preferences": {
"theme": "dark"
}
}
Does it replace the entire preferences object (removing notifications, language etc.)? Or merge into it? The answer is implementation-specific and often undocumented. JSON Merge Patch defines this precisely.
JSON Merge Patch (RFC 7396): the simpler standard
JSON Merge Patch uses a simple rule: the provided object is merged into the existing resource. To remove a key, set it to null.
Rules:
- For each key in the patch:
- If the value is
null: remove that key from the resource - If the value is a JSON object: recursively merge
- Otherwise: replace the value in the resource
- If the value is
Example:
Original resource:
{
"name": "Alice",
"email": "alice@example.com",
"preferences": {
"theme": "light",
"notifications": true,
"language": "en"
},
"active": true
}
Merge Patch:
{
"email": "alice@newdomain.com",
"preferences": {
"theme": "dark"
},
"active": null
}
Result:
{
"name": "Alice",
"email": "alice@newdomain.com",
"preferences": {
"theme": "dark",
"notifications": true,
"language": "en"
}
}
emailwas updatedpreferences.themewas updated; other preferences keys preserved (recursive merge)activewas removed (null means delete)namewas untouched (not in patch)
Content-Type: application/merge-patch+json
Limitation: you can't use Merge Patch on resources where null is a legitimate field value (because null means "delete"). For those resources, JSON Patch is necessary.
JSON Patch (RFC 6902): operations-based
JSON Patch expresses changes as an array of operation objects. Each operation has a op, path, and (where applicable) value or from.
Operations:
| Op | Action |
|---|---|
add |
Add a value at path (creates key if absent, appends to array) |
remove |
Remove the value at path |
replace |
Replace the value at path (must exist) |
move |
Move a value from one path to another |
copy |
Copy a value from one path to another |
test |
Test that a value at path equals a given value (fails patch if not) |
JSON Pointer (RFC 6901): paths use the /key/subkey format. /0 for array index 0. /- for "append to end of array".
Example operations:
[
{ "op": "replace", "path": "/email", "value": "alice@newdomain.com" },
{ "op": "replace", "path": "/preferences/theme", "value": "dark" },
{ "op": "remove", "path": "/active" },
{ "op": "add", "path": "/preferences/timezone", "value": "Europe/London" },
{ "op": "test", "path": "/name", "value": "Alice" }
]
The test operation: checks a precondition before applying the patch. If Alice's name has changed since you fetched the resource, the test fails and the entire patch is rejected atomically. This is the optimistic concurrency mechanism built into JSON Patch β you can verify state before modifying it, without needing a separate version field.
Content-Type: application/json-patch+json
JSON Pointer (RFC 6901)
JSON Pointer is the path syntax used in JSON Patch and referenced in JSON Schema $ref resolution.
Syntax:
- Empty string
"": refers to the whole document /foo: the keyfoo/foo/bar: nested keybarwithinfoo/foo/0: index 0 of arrayfoo/a~1b: keya/b(the/in the key is escaped as~1)/a~0b: keya~b(the~is escaped as~0)
{
"store": {
"books": [
{"title": "Foundation", "price": 12.99},
{"title": "Dune", "price": 14.99}
]
}
}
/store/books/0/titleβ"Foundation"/store/books/1/priceβ14.99/store/books/-β append position (foraddoperations)
JSON Path: querying, not just addressing
JSON Pointer addresses a specific location. JSON Path (RFC 9535, standardised 2024) queries β it can match multiple nodes and supports filtering.
Syntax examples:
$.store.books[*].title // All book titles
$.store.books[0].title // First book title
$.store.books[?(@.price < 13)] // Books cheaper than $13
$..title // All 'title' values anywhere in document
$.store.books[-1:] // Last book
Where JSON Path is used:
- Kubernetes: pod spec JSONPath queries (
kubectl get pods -o jsonpath='{.items[*].metadata.name}') - AWS IAM: resource conditions reference JSON Path
- Grafana: JSON datasource queries
- OpenAPI / JSON Schema:
jsonpathfilters in some tools
Library support
JavaScript (fast-json-patch):
const jsonpatch = require('fast-json-patch');
const document = { name: "Alice", email: "alice@example.com" };
const patch = [
{ op: "replace", path: "/email", value: "alice@new.com" },
{ op: "add", path: "/age", value: 30 }
];
const result = jsonpatch.applyPatch(document, patch);
Python (jsonpatch):
import jsonpatch
doc = {"name": "Alice", "email": "alice@example.com"}
patch = jsonpatch.JsonPatch([
{"op": "replace", "path": "/email", "value": "alice@new.com"},
{"op": "add", "path": "/age", "value": 30}
])
result = patch.apply(doc)
How to use the JSON Unescape tool on sadiqbd.com
When working with JSON Patch:
- Paste escaped JSON Patch payloads from API logs β remove escaping to read the operations clearly
- Clean double-encoded patches β when a JSON Patch array has been JSON-stringified (common when embedded in another JSON body)
- Debug path issues β format the patch operations to verify paths are correct
Frequently Asked Questions
Should I use JSON Patch or JSON Merge Patch for a new API? JSON Merge Patch for most cases β simpler, easier to understand, and sufficient for the majority of update patterns. JSON Patch when you need: array element modification by index, atomic test-and-set operations, move/copy operations, or when null is a valid field value in your schema.
Does HTTP PATCH require a specific body format?
No β PATCH is defined to accept any format that the server understands. The Content-Type header specifies the format: application/json-patch+json for JSON Patch, application/merge-patch+json for Merge Patch, or application/json for informal partial updates.
Is the JSON Unescape tool free? Yes β completely free, no sign-up required.
Try the JSON Unescape tool free at sadiqbd.com β remove JSON escaping, decode double-encoded JSON, and clean any escaped JSON string instantly.