JSON / Data·6 min read·By the StackUtils Team

JSON Diff and Patch: Comparing and Merging JSON Documents

When two JSON documents diverge, you need a reliable way to compare, diff, and merge them. This guide covers RFC 6902 JSON Patch, RFC 7396 JSON Merge Patch, deep equality, and practical diffing strategies.

When You Need to Diff JSON

Common scenarios where JSON comparison comes up in real projects:

  • Debugging API responses that changed between versions
  • Reviewing config file changes in infrastructure-as-code (Terraform state, Kubernetes manifests)
  • Implementing optimistic UI updates and conflict resolution
  • Audit logs — storing what changed rather than the full document
  • Feature flags — sending only the delta to clients instead of the full config

Deep Equality: The Foundation

Shallow equality (===) only compares references for objects. Two objects with identical values are not strictly equal in JavaScript:

const a = { name: "Alice", tags: ["admin"] };
const b = { name: "Alice", tags: ["admin"] };

console.log(a === b);              // false — different references
console.log(JSON.stringify(a) === JSON.stringify(b)); // true — but fragile (key order!)

// Robust deep equality — lodash
import { isEqual } from 'lodash';
console.log(isEqual(a, b));        // true ✅

// Or write your own for simple cases:
function deepEqual(a, b) {
  if (a === b) return true;
  if (typeof a !== 'object' || typeof b !== 'object') return false;
  if (a === null || b === null) return false;
  const keysA = Object.keys(a), keysB = Object.keys(b);
  if (keysA.length !== keysB.length) return false;
  return keysA.every(k => deepEqual(a[k], b[k]));
}

⚠️ JSON.stringify comparison breaks when object keys are in different order. Always use a proper deep-equality function.

RFC 6902: JSON Patch

JSON Patch (RFC 6902) is a standard format for describing changes to a JSON document as a sequence of operations. It is used in REST APIs with the PATCH HTTP method and in collaborative editing systems.

OperationExampleEffect
add{ "op":"add", "path":"/tags/0", "value":"vip" }Insert value at JSON Pointer path
remove{ "op":"remove", "path":"/address" }Delete the value at path
replace{ "op":"replace", "path":"/name", "value":"Bob" }Replace value at path
move{ "op":"move", "from":"/a", "path":"/b" }Move value from one path to another
copy{ "op":"copy", "from":"/a", "path":"/b" }Copy value to a new path
test{ "op":"test", "path":"/name", "value":"Alice" }Assert value equals — abort patch if not
import jsonpatch from 'fast-json-patch';

const original = { name: "Alice", role: "user", tags: ["active"] };
const modified  = { name: "Alice", role: "admin", tags: ["active", "vip"] };

// Generate the patch
const patch = jsonpatch.compare(original, modified);
// [
//   { op: 'replace', path: '/role', value: 'admin' },
//   { op: 'add', path: '/tags/1', value: 'vip' },
// ]

// Apply the patch to the original
const result = jsonpatch.applyPatch(original, patch).newDocument;

RFC 7396: JSON Merge Patch

Simpler than JSON Patch — a merge patch is just a partial JSON document. Keys with non-null values are added or replaced; keys with null values are deleted; missing keys are untouched.

const original   = { name: "Alice", role: "user", email: "alice@example.com" };
const mergePatch = { role: "admin", email: null };
// email: null → delete email
// role: "admin" → replace role
// name: missing → keep as-is

// Result:
const result = { name: "Alice", role: "admin" };

// Simple implementation:
function applyMergePatch(target, patch) {
  const result = { ...target };
  for (const [key, value] of Object.entries(patch)) {
    if (value === null) delete result[key];
    else if (typeof value === 'object' && !Array.isArray(value))
      result[key] = applyMergePatch(result[key] ?? {}, value);
    else result[key] = value;
  }
  return result;
}

Limitation: you cannot use JSON Merge Patch to explicitly set a value to null, or to remove items from arrays. For those cases, use JSON Patch (RFC 6902).

Which Approach to Use

ScenarioRecommended approach
Simple field updates, no nulls or array opsJSON Merge Patch (RFC 7396) — simpler API and payload
Complex changes: array inserts, moves, nullsJSON Patch (RFC 6902)
Visual comparison for debugging / code reviewA diff tool — show added/removed/changed lines
Storing change history in a databaseJSON Patch — store the patch array per revision
Collaborative real-time editing (like Google Docs)Operational Transformation or CRDT — beyond JSON Patch

Compare two JSON documents visually

Paste two JSON objects to see a side-by-side diff with added, removed, and changed fields highlighted.

Open JSON Diff →