JSON Merge Patch — Partial Updates to JSON Documents (RFC 7396)
JSON Merge Patch (RFC 7396) is a simple format for partial JSON updates. A null value removes a key; other values replace or add. Here's how JSON Merge Patch works, how it...
JSON Merge Patch (RFC 7396) defines a simple way to describe partial changes to a JSON document. You send only the keys you want to change — a null value removes a key, any other value replaces or adds it.
Use the JSON Diff Tool to compare JSON documents and visualize differences.
How JSON Merge Patch works
The merge patch is itself a JSON object. When applied to a target document:
- If a key in the patch is
null— remove that key from the target - If a key exists in the patch — set that key in the target to the patch’s value
- If a key doesn’t exist in the patch — leave the target unchanged
Example
Original document:
{
"title": "Hello World",
"author": "Alice",
"tags": ["intro", "tutorial"],
"metadata": {
"views": 100,
"published": true
}
}
Merge patch:
{
"title": "Hello World Updated",
"author": null,
"metadata": {
"views": 250
}
}
Result:
{
"title": "Hello World Updated",
"tags": ["intro", "tutorial"],
"metadata": {
"views": 250
}
}
Changes made:
titleupdated to new valueauthorremoved (null)tagsunchanged (not in patch)metadata.viewsupdated — butmetadata.publishedis GONE (see caveat below)
The nested object caveat
This is the biggest gotcha: merge patch replaces nested objects entirely, it doesn’t merge them recursively:
// Original:
{
"settings": {
"theme": "dark",
"language": "en",
"notifications": true
}
}
// Patch:
{
"settings": {
"theme": "light"
}
}
// Result — language and notifications are GONE:
{
"settings": {
"theme": "light"
}
}
To update a single nested key while preserving others, your patch must include all existing keys you want to keep, or restructure your data.
Implementing JSON Merge Patch
JavaScript
function mergePatch(target, patch) {
if (typeof patch !== 'object' || patch === null) {
return patch;
}
const result = typeof target === 'object' && target !== null
? { ...target }
: {};
for (const [key, value] of Object.entries(patch)) {
if (value === null) {
delete result[key];
} else {
result[key] = mergePatch(result[key], value);
}
}
return result;
}
const original = { a: 1, b: 2, c: { d: 3, e: 4 } };
const patch = { b: null, c: { d: 10 } };
mergePatch(original, patch);
// { a: 1, c: { d: 10 } }
// Note: c.e is gone because c was replaced, not merged
Python
import copy
def merge_patch(target, patch):
if not isinstance(patch, dict):
return patch
result = copy.deepcopy(target) if isinstance(target, dict) else {}
for key, value in patch.items():
if value is None:
result.pop(key, None)
else:
result[key] = merge_patch(result.get(key), value)
return result
original = {"a": 1, "b": {"c": 2, "d": 3}}
patch = {"b": {"c": 10}, "e": 5}
merge_patch(original, patch)
# {"a": 1, "b": {"c": 10}, "e": 5}
# b.d is gone
Node.js with json-merge-patch library
import { apply } from 'json-merge-patch';
const original = { a: 1, b: 2, c: 3 };
const patch = { b: null, d: 4 };
apply(original, patch);
// { a: 1, c: 3, d: 4 }
Using JSON Merge Patch in REST APIs
HTTP PATCH requests with Content-Type application/merge-patch+json:
PATCH /articles/123 HTTP/1.1
Content-Type: application/merge-patch+json
{
"title": "Updated Title",
"draft": null
}
Express.js handler:
app.patch('/articles/:id', async (req, res) => {
const patch = req.body; // JSON Merge Patch
const existing = await db.articles.findById(req.params.id);
const updated = mergePatch(existing, patch);
await db.articles.update(req.params.id, updated);
res.json(updated);
});
JSON Merge Patch vs JSON Patch (RFC 6902)
| Feature | Merge Patch (RFC 7396) | JSON Patch (RFC 6902) |
|---|---|---|
| Format | JSON object | Array of operations |
| Operations | Replace, delete, add (implicit) | add, remove, replace, move, copy, test |
| Can’t remove from arrays | Yes — replacing array removes it | No — can target array indices |
| Nested objects | Replaces, not merges | Can target specific paths |
| Complexity | Simple | More complex |
| Use case | Simple partial updates | Fine-grained operations |
// JSON Patch equivalent of setting title and removing author:
[
{ "op": "replace", "path": "/title", "value": "New Title" },
{ "op": "remove", "path": "/author" }
]
When to use each
Use JSON Merge Patch when:
- Your document is mostly flat (shallow nesting)
- You want a simple, readable diff format
- You’re updating a handful of top-level fields
- You need something easy to implement
Use JSON Patch when:
- You need to add/remove array items by index
- You need atomic operations (test before apply)
- You need move/copy operations
- You need precise control over deep nested paths
Generating a merge patch
To generate the patch needed to transform document A into document B:
function generateMergePatch(original, updated) {
if (typeof original !== 'object' || typeof updated !== 'object') {
return original === updated ? undefined : updated;
}
const patch = {};
// Keys to remove (in original but not in updated):
for (const key of Object.keys(original)) {
if (!(key in updated)) {
patch[key] = null;
}
}
// Keys to add or update:
for (const [key, value] of Object.entries(updated)) {
if (JSON.stringify(original[key]) !== JSON.stringify(value)) {
patch[key] = value;
}
}
return Object.keys(patch).length > 0 ? patch : undefined;
}
const a = { x: 1, y: 2, z: { a: 3 } };
const b = { x: 1, z: { a: 4 }, w: 5 };
generateMergePatch(a, b);
// { y: null, z: { a: 4 }, w: 5 }
Related tools
- JSON Diff Tool — compare JSON documents visually
- JSON Patch RFC 6902 — operation-based JSON patching
- Comparing JSON Structurally — diff approaches
Related posts
- Comparing JSON Structurally (Not Just as Strings) — Two JSON documents can be byte-different and semantically identical. Or byte-ide…
- How to Compare JSON Objects — Deep Equality and Diff in JavaScript and Python — Comparing JSON objects with == won't work for deep equality. Here's how to deep-…
- Detecting Changes in JSON Data — Audit Logs, Diffs, and Change Tracking — Detecting what changed in a JSON document is essential for audit logs, versionin…
- JSON Diff Tool — Compare Two JSON Objects and Find Differences — A JSON diff tool compares two JSON structures semantically, not textually. It fi…
- JSON Patch (RFC 6902) — Operation-Based JSON Document Updates — JSON Patch (RFC 6902) defines a sequence of operations (add, remove, replace, mo…
Related tool
Compare two JSON objects structurally with field-by-field diff.
Written by Mian Ali Khalid. Part of the Data & Format pillar.