How to Compare Two JSON Files (and Read the Diff Correctly)

Compare two JSON files with a worked browser example or a runnable Python script. Learn which differences come from values, object-key order and array order.

A test of mine once failed because two API responses "didn't match" — except they did. The data was identical; the server had just serialized the keys in a different order. My assertion was JSON.stringify(actual) === JSON.stringify(expected), and that check is wrong more often than people realize. If you compare JSON — for regression tests, config drift, or reviewing an API change — here's how to do it correctly.

Compare two JSON files: a worked example

Start with these small files. They contain one changed value, one removed property and one added property. The object keys also appear in a different order.

Download before.json · Download after.json

before.json

{
  "id": 42,
  "name": "Ada",
  "active": true,
  "legacyCode": "A1",
  "roles": [
    "reader",
    "editor"
  ]
}

after.json

{
  "roles": [
    "reader",
    "editor"
  ],
  "id": 42,
  "name": "Ada Lovelace",
  "active": true,
  "email": "ada@example.com"
}
  1. Open JSON Compare. Paste the contents of before.json into the left editor and after.json into the right editor.
  2. For the side-by-side comparison, enable Ignore key order. Leave Ignore array order off: the order of our roles array is unchanged.
  3. Click Compare to inspect the side-by-side result, then Semantic Diff for the JSON Patch-style operations.
  4. Use the paths and values below to check what actually changed. Object-key reordering should not create an extra semantic change.
OperationPathBefore → after
replace/nameAda → Ada Lovelace
remove/legacyCodeA1 → property absent
add/emailproperty absent → ada@example.com

The operation order may differ. The unchanged id, active and roles values should not appear as changes. These paths identify properties in the JSON document; a removed property is different from a property whose value is null.

What if the array order changes?

Change the second file's roles to ["editor", "reader"]. That is an ordered-array change. If your application treats roles as an unordered collection, enable Ignore array order for the side-by-side comparison. The Semantic Diff view remains index-sensitive; the toggle does not turn its JSON Patch output into an unordered comparison.

Why JSON.stringify(a) === JSON.stringify(b) lies

JSON objects are unordered by specification — {"a":1,"b":2} and {"b":2,"a":1} represent the same data. But JSON.stringify preserves insertion order, so the two produce different strings and a string comparison reports a difference that isn't real:

// Order-sensitive — falsely reports a difference
JSON.stringify({a: 1, b: 2}) === JSON.stringify({b: 2, a: 1});  // false

// Canonicalize keys recursively, THEN compare
function canonical(v) {
  if (Array.isArray(v)) return v.map(canonical);
  if (v && typeof v === "object") {
    return Object.keys(v).sort().reduce((o, k) => (o[k] = canonical(v[k]), o), Object.create(null));
  }
  return v;
}
const equal = (a, b) => JSON.stringify(canonical(a)) === JSON.stringify(canonical(b));

This helper is for values obtained by parsing JSON. It sorts object keys recursively and preserves keys such as __proto__, while keeping array order significant. It is not a general comparison for arbitrary JavaScript objects. Standard JavaScript parsing can lose precision for large integers, so use a lossless parser when exact large-number comparison matters.

The gotchas a naive diff trips on

  • Key order: objects are unordered; reordering is not a real change (but string equality says it is).
  • Array order: arrays are ordered — [1, 2] ≠ [2, 1]. Only sort them if you genuinely mean "set," not "list."
  • Type coercion: 1 (number) and "1" (string) are different in JSON. A loose compare that coerces them will miss a real change.
  • null vs missing: a key set to null is not the same as a key that's absent.
  • Number normalization: 1 vs 1.0, or float precision, can read as changes depending on the serializer.

Textual diff vs semantic diff

The single biggest mistake is using a line-by-line diff (like git diff) on JSON. Reformat or reorder the file and the entire thing lights up red. You want a semantic diff that compares by key path.

ApproachWhat it showsBest for
Textual (line diff)Character/line changes — noisy on any reformatting or reorderingTwo files already in identical format
Semantic (by path)changed at users[0].email, fields added/removedRegression tests, config drift, API version reviews

Compare two JSON files in Python

Save the two sample files and the following script as compare_json.py in the same folder. In your Python environment, install DeepDiff:

python -m pip install deepdiff
import json
from pathlib import Path
from deepdiff import DeepDiff

folder = Path(__file__).resolve().parent
with (folder / "before.json").open(encoding="utf-8") as f:
    before = json.load(f)
with (folder / "after.json").open(encoding="utf-8") as f:
    after = json.load(f)

diff = DeepDiff(before, after, ignore_order=False)
print(diff.to_json(indent=2))

Run it with:

python compare_json.py

The result contains values_changed for root['name'], dictionary_item_removed for root['legacyCode'], and dictionary_item_added for root['email']. Object-key order is ignored; array order is significant.

Use ignore_order=True only when sequence is irrelevant for all arrays being compared. If duplicate counts matter, also consider DeepDiff's report_repetition=True. This script loads both files into memory; it is not a streaming solution for multi-gigabyte data.

Choose the comparison mode you need

Use Compare for side-by-side text inspection, with key-order and array-order normalization when appropriate. Use Semantic Diff for changes expressed as JSON Patch-style operations. The two views answer different questions: changes in presentation versus changes to the data structure.

The JSON Compare tool processes comparisons in your browser (disclosure: I built it). For a reusable description of a change, see JSON Patch versus JSON Merge Patch. Consult the privacy policy for information about analytics and third-party services.

FAQs

Does key order matter when comparing JSON?
By spec, object keys are unordered, so reordering isn't a real change — but JSON.stringify equality treats it as one. Use a canonical compare (sort keys first) or a semantic diff.

Is [1, 2] the same as [2, 1]?
No — array order is significant in JSON. Only sort both sides first if you're treating the array as an unordered set.

Why does my diff show everything as changed?
Almost always reformatting or key reordering. Switch from a line-by-line diff to a semantic/structural one that compares by key path.

How do I compare two large JSON files?
Use a semantic diff that reports changes by path (a tool, or DeepDiff in Python) rather than scrolling a line diff. Validate both files parse first.

Related articles