X Xerobit

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...

Mian Ali Khalid · · 5 min read
Use the tool
JSON Diff
Compare two JSON objects structurally with field-by-field diff.
Open JSON Diff →

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 posts

Related tool

JSON Diff

Compare two JSON objects structurally with field-by-field diff.

Written by Mian Ali Khalid. Part of the Data & Format pillar.