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-compare JSON in JavaScript and Python, detect differences, find changed keys, and generate...
You can’t compare JSON objects with == or === in JavaScript — object references are compared, not values. You need deep equality. Here’s how to properly compare JSON objects and find their differences.
Use the JSON Diff Tool to compare JSON documents visually.
Why === fails for JSON
const a = { name: 'Alice', age: 30 };
const b = { name: 'Alice', age: 30 };
a === b // false — different object references
a == b // false — same issue
Even after JSON.parse, objects with identical content aren’t === equal.
Simple deep equality check
The quickest way: serialize both to JSON strings and compare:
function jsonEqual(a, b) {
return JSON.stringify(a) === JSON.stringify(b);
}
jsonEqual({ a: 1, b: 2 }, { a: 1, b: 2 }); // true
jsonEqual({ a: 1, b: 2 }, { b: 2, a: 1 }); // false! key order matters
The caveat: JSON.stringify is sensitive to key ordering. { a: 1, b: 2 } and { b: 2, a: 1 } serialize differently.
Sort-independent comparison
function deepEqual(a, b) {
if (a === b) return true;
if (typeof a !== typeof b) return false;
if (typeof a !== 'object' || a === null || b === null) return false;
if (Array.isArray(a) !== Array.isArray(b)) return false;
if (Array.isArray(a)) {
if (a.length !== b.length) return false;
return a.every((item, i) => deepEqual(item, b[i]));
}
const keysA = Object.keys(a).sort();
const keysB = Object.keys(b).sort();
if (keysA.length !== keysB.length) return false;
if (JSON.stringify(keysA) !== JSON.stringify(keysB)) return false;
return keysA.every(key => deepEqual(a[key], b[key]));
}
deepEqual({ a: 1, b: 2 }, { b: 2, a: 1 }); // true
deepEqual([1, 2, 3], [1, 2, 3]); // true
deepEqual([1, 2, 3], [1, 3, 2]); // false
Using a library
// lodash:
import { isEqual } from 'lodash';
isEqual({ a: 1, b: { c: 2 } }, { a: 1, b: { c: 2 } }); // true
// fast-deep-equal:
import equal from 'fast-deep-equal';
equal({ a: 1 }, { a: 1 }); // true
Finding differences between JSON objects
Deep equality tells you if objects differ, but not how. Here’s how to find changed paths:
JavaScript: generate a diff
function diffJson(a, b, path = '') {
const diffs = [];
// Keys in a but not b (removed):
for (const key of Object.keys(a)) {
const fullPath = path ? `${path}.${key}` : key;
if (!(key in b)) {
diffs.push({ path: fullPath, type: 'removed', oldValue: a[key] });
} else if (typeof a[key] === 'object' && typeof b[key] === 'object'
&& a[key] !== null && b[key] !== null) {
diffs.push(...diffJson(a[key], b[key], fullPath));
} else if (a[key] !== b[key]) {
diffs.push({ path: fullPath, type: 'changed', oldValue: a[key], newValue: b[key] });
}
}
// Keys in b but not a (added):
for (const key of Object.keys(b)) {
if (!(key in a)) {
const fullPath = path ? `${path}.${key}` : key;
diffs.push({ path: fullPath, type: 'added', newValue: b[key] });
}
}
return diffs;
}
const a = { name: 'Alice', age: 30, address: { city: 'London', zip: 'SW1' } };
const b = { name: 'Alice', age: 31, address: { city: 'Manchester' }, email: 'a@b.com' };
diffJson(a, b);
// [
// { path: 'age', type: 'changed', oldValue: 30, newValue: 31 },
// { path: 'address.city', type: 'changed', oldValue: 'London', newValue: 'Manchester' },
// { path: 'address.zip', type: 'removed', oldValue: 'SW1' },
// { path: 'email', type: 'added', newValue: 'a@b.com' }
// ]
Using fast-json-patch
import { compare } from 'fast-json-patch';
const original = { a: 1, b: { c: 2, d: 3 }, e: [1, 2] };
const updated = { a: 1, b: { c: 5 }, e: [1, 2, 3], f: 'new' };
const patch = compare(original, updated);
// [
// { op: 'replace', path: '/b/c', value: 5 },
// { op: 'remove', path: '/b/d' },
// { op: 'add', path: '/e/2', value: 3 },
// { op: 'add', path: '/f', value: 'new' }
// ]
Python comparison
Deep equality
# Python dict comparison works deeply by default:
a = {"name": "Alice", "age": 30, "nested": {"x": 1}}
b = {"name": "Alice", "age": 30, "nested": {"x": 1}}
a == b # True — Python dicts compare by value, not reference
# Key order doesn't matter:
{"a": 1, "b": 2} == {"b": 2, "a": 1} # True
# Lists are order-sensitive:
{"tags": [1, 2, 3]} == {"tags": [3, 2, 1]} # False
Finding differences
def diff_json(a, b, path=""):
diffs = []
if isinstance(a, dict) and isinstance(b, dict):
for key in a:
full_path = f"{path}.{key}" if path else key
if key not in b:
diffs.append({"path": full_path, "type": "removed", "old": a[key]})
else:
diffs.extend(diff_json(a[key], b[key], full_path))
for key in b:
if key not in a:
full_path = f"{path}.{key}" if path else key
diffs.append({"path": full_path, "type": "added", "new": b[key]})
elif a != b:
diffs.append({"path": path, "type": "changed", "old": a, "new": b})
return diffs
a = {"name": "Alice", "age": 30, "city": "London"}
b = {"name": "Alice", "age": 31, "email": "a@b.com"}
diff_json(a, b)
# [
# {"path": "age", "type": "changed", "old": 30, "new": 31},
# {"path": "city", "type": "removed", "old": "London"},
# {"path": "email", "type": "added", "new": "a@b.com"}
# ]
Using deepdiff
from deepdiff import DeepDiff
original = {"a": 1, "b": {"c": 2, "d": 3}}
updated = {"a": 1, "b": {"c": 5, "e": 6}}
diff = DeepDiff(original, updated)
print(diff)
# {
# 'values_changed': {"root['b']['c']": {'new_value': 5, 'old_value': 2}},
# 'dictionary_item_removed': {"root['b']['d']"},
# 'dictionary_item_added': {"root['b']['e']"}
# }
Comparing two JSON files
JavaScript
import { readFileSync } from 'fs';
import { compare } from 'fast-json-patch';
const a = JSON.parse(readFileSync('a.json', 'utf8'));
const b = JSON.parse(readFileSync('b.json', 'utf8'));
const patch = compare(a, b);
if (patch.length === 0) {
console.log('Files are identical');
} else {
console.log('Differences:', JSON.stringify(patch, null, 2));
}
Python
import json
from deepdiff import DeepDiff
with open('a.json') as f:
a = json.load(f)
with open('b.json') as f:
b = json.load(f)
diff = DeepDiff(a, b, ignore_order=True)
if not diff:
print("Files are identical")
else:
print(diff.to_json(indent=2))
Related tools
- JSON Diff Tool — compare JSON documents visually online
- JSON Merge Patch — partial JSON updates (RFC 7396)
- JSON Patch RFC 6902 — operation-based JSON updates
Related posts
- Comparing JSON Structurally (Not Just as Strings) — Two JSON documents can be byte-different and semantically identical. Or byte-ide…
- 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 API Response Format — Structuring REST API Responses — A consistent JSON response format makes APIs predictable and easier to consume. …
- JSON Diff Tool — Compare Two JSON Objects and Find Differences — A JSON diff tool compares two JSON structures semantically, not textually. It fi…
- 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 …
Related tool
Compare two JSON objects structurally with field-by-field diff.
Written by Mian Ali Khalid. Part of the Data & Format pillar.