Skip to main content

Command Palette

Search for a command to run...

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

Updated
4 min read
A
Software engineer building free, client-side developer tools. Creator of JSONViewerTool.com — 40+ JSON tools (viewer, formatter, validator, escape, compare, converters) that run 100% in your browser with no uploads. Sharing practical guides on JSON, APIs, and dev tooling.

Why stringify-equality lies, the gotchas that fool naive diffs (key order, array order, types), and how to read a semantic diff.

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.

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), {});
  }
  return v;
}
const equal = (a, b) => JSON.stringify(canonical(a)) === JSON.stringify(canonical(b));

This sorts keys at every level so object reordering no longer counts as a change -- while keeping array order significant, which it should be.

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.

Comparing JSON correctly in code

In Python, sort keys for a canonical compare, or use DeepDiff for a real field-by-field report:

import json

# Naive string compare is order-sensitive too:
json.dumps({"a": 1, "b": 2}) == json.dumps({"b": 2, "a": 1})   # False

# Canonical compare -- sort keys at every level:
def equal(a, b):
    return json.dumps(a, sort_keys=True) == json.dumps(b, sort_keys=True)

# A real, path-level diff:
from deepdiff import DeepDiff
DeepDiff(old, new)   # -> {'values_changed': {...}, 'dictionary_item_added': [...]}

A faster way: a visual semantic diff (no script)

When I just need to see what changed between two payloads -- in a code review or while debugging a failing test -- a visual diff beats writing a comparison script. Our free JSON Compare tool does a side-by-side semantic diff: it highlights added, removed, and changed values by path, ignores cosmetic reordering, and runs entirely in your browser (disclosure: I built it). For applying a known set of changes rather than just viewing them, JSON Patch (RFC 6902) is the structured counterpart.

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.