X Xerobit

JSON Diff Libraries — Best Tools for Diffing JSON in JavaScript and Python

Several libraries make JSON diffing easier than writing your own. Here's a comparison of the best JavaScript and Python JSON diff libraries — fast-json-patch, jsondiffpatch,...

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

Several well-maintained libraries handle JSON diffing better than rolling your own. They handle edge cases (arrays, types, nested objects) and produce structured output you can act on.

Use the JSON Diff Tool to compare JSON documents visually without installing anything.

JavaScript libraries

fast-json-patch

The most popular RFC 6902 JSON Patch library. Generates and applies JSON Patch operations.

npm install fast-json-patch
import { compare, applyPatch, validate } from 'fast-json-patch';

const original = { a: 1, b: { c: 2 }, tags: ['x', 'y'] };
const updated  = { a: 2, b: { c: 2, d: 3 }, tags: ['x'] };

// Generate patch:
const patch = compare(original, updated);
// [
//   { op: 'replace', path: '/a', value: 2 },
//   { op: 'add',     path: '/b/d', value: 3 },
//   { op: 'remove',  path: '/tags/1' }
// ]

// Apply patch:
const result = applyPatch(original, patch).newDocument;

// Validate patch is applicable:
const errors = validate(patch, original);

Best for: REST APIs, RFC 6902 compliance, server-side state patching.

jsondiffpatch

Richer diff format with delta encoding. Shows arrays as sequences of additions/deletions.

npm install jsondiffpatch
import { diff, patch, reverse, formatters } from 'jsondiffpatch';

const a = { name: 'Alice', scores: [10, 20, 30] };
const b = { name: 'Bob',   scores: [10, 25, 30, 40] };

const delta = diff(a, b);
// {
//   name: ['Alice', 'Bob'],              // [old, new]
//   scores: { 1: [20, 25], 3: [40, 0, 0] }  // [old, new] or [new, 0, 0] for added
// }

// Apply:
const result = patch(a, delta);  // { name: 'Bob', scores: [10, 25, 30, 40] }

// Reverse the diff (undo):
const reversed = reverse(delta);
const original = patch(b, reversed);

// HTML visualization:
console.log(formatters.html.format(delta, a));

// Console visualization:
formatters.console.log(delta);

Best for: visual diffs, undo/redo systems, collaborative editing.

lodash isEqual + custom diff

For simple deep equality without a library:

import { isEqual, get, set, keys } from 'lodash';

function deepDiff(a, b, prefix = '') {
  const diff = {};
  
  const allKeys = [...new Set([...keys(a), ...keys(b)])];
  
  for (const key of allKeys) {
    const path = prefix ? `${prefix}.${key}` : key;
    
    if (!isEqual(a[key], b[key])) {
      if (typeof a[key] === 'object' && typeof b[key] === 'object'
          && !Array.isArray(a[key]) && !Array.isArray(b[key])) {
        Object.assign(diff, deepDiff(a[key], b[key], path));
      } else {
        diff[path] = { from: a[key], to: b[key] };
      }
    }
  }
  
  return diff;
}

microdiff

Tiny (1KB), fast, no dependencies:

npm install microdiff
import diff from 'microdiff';

const original = { a: 1, b: { c: 2 } };
const updated  = { a: 2, b: { c: 3, d: 4 } };

diff(original, updated);
// [
//   { type: 'CHANGE', path: ['a'], value: 2, oldValue: 1 },
//   { type: 'CHANGE', path: ['b', 'c'], value: 3, oldValue: 2 },
//   { type: 'CREATE', path: ['b', 'd'], value: 4 }
// ]

Change types: CREATE, REMOVE, CHANGE.

Python libraries

deepdiff

The most feature-rich Python JSON diff library:

pip install deepdiff
from deepdiff import DeepDiff

original = {"name": "Alice", "scores": [10, 20, 30], "active": True}
updated  = {"name": "Bob",   "scores": [10, 25, 30, 40]}

diff = DeepDiff(original, updated)
print(diff)
# {
#   'values_changed': {
#     "root['name']": {'new_value': 'Bob', 'old_value': 'Alice'},
#     "root['scores'][1]": {'new_value': 25, 'old_value': 20}
#   },
#   'iterable_item_added': {"root['scores'][3]": 40},
#   'dictionary_item_removed': {"root['active']"}
# }

# Get a simpler view:
diff.to_dict()

# Ignore order in lists:
DeepDiff(original, updated, ignore_order=True)

# Ignore specific types of changes:
DeepDiff(original, updated, exclude_types=[int])

# Generate a JSON-serializable report:
import json
json.loads(diff.to_json())

jsonpatch

RFC 6902 implementation for Python:

pip install jsonpatch
import jsonpatch

original = {"a": 1, "b": 2, "c": [1, 2, 3]}
updated  = {"a": 10, "c": [1, 2]}

# Generate patch:
patch = jsonpatch.make_patch(original, updated)
print(patch.to_string())
# [{"op": "replace", "path": "/a", "value": 10},
#  {"op": "remove", "path": "/b"},
#  {"op": "remove", "path": "/c/2"}]

# Apply patch:
result = patch.apply(original)
# {"a": 10, "c": [1, 2]}

# Apply from string:
jsonpatch.apply_patch(original, '[{"op": "add", "path": "/d", "value": 4}]')

jsondiff

Simple, readable diff format:

pip install jsondiff
from jsondiff import diff, patch

a = {"x": 1, "y": [1, 2, 3]}
b = {"x": 2, "y": [1, 4, 3], "z": 5}

d = diff(a, b)
# {'x': 2, 'y': {1: 4}, 'z': 5}
# Missing keys = removed, new keys = added, shared = changed

# Apply:
result = patch(a, d)

Choosing a library

LibraryLanguageFormatUse Case
fast-json-patchJSRFC 6902REST APIs, HTTP PATCH
jsondiffpatchJSDeltaVisual diffs, undo/redo
microdiffJSCustomSimple, small bundle
deepdiffPythonCustomAnalysis, reporting
jsonpatchPythonRFC 6902REST APIs
jsondiffPythonDeltaReadable diffs

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.