All articles

How to Compare JSON Objects and Spot Differences

5 min read
JSONdiffcomparison

Comparing two JSON documents to find what changed is a surprisingly common developer task: debugging an API whose response changed, reviewing a configuration update, checking that a migration produced the expected output, or inspecting the difference between two versions of a data file. Here are the main techniques, from manual to automated.

Why naive string comparison fails

The first instinct is to compare the JSON strings directly. This almost always produces false positives, because JSON has many semantically equivalent representations:

  • Keys in the same object can be in any order
  • Whitespace and indentation are insignificant
  • Numbers can be represented as 1, 1.0, or 1e0
  • Arrays can have the same elements in different orders — whether that is a "difference" depends on your domain

Two JSON documents that represent identical data can look completely different as strings. A useful JSON diff tool normalises both documents before comparing them.

Normalisation before comparison

Normalisation means transforming both documents into a canonical form before diffing them. The minimum useful normalisation:

  • Sort object keys alphabetically — so {"b":1,"a":2} and {"a":2,"b":1} compare as equal
  • Standardise whitespace — reformat with consistent indentation
  • Normalise number representation — strip trailing zeros, use consistent notation

After normalising, a line-by-line text diff on the formatted output is usually sufficient for human review.

Reading a JSON diff

A structured JSON diff typically shows:

  • Added — keys or array elements present in the new version but not the old
  • Removed — keys or array elements present in the old version but not the new
  • Changed — keys present in both versions but with different values
  • Unchanged — keys present in both versions with the same value (often collapsed)

For nested documents, the diff is recursive — a changed nested value shows the path from the root (e.g. user.address.city) so you can immediately locate the change in context.

Array diffing is harder

Arrays are the most complex part of JSON diffing. There are two reasonable approaches, and which is correct depends on the data:

  • Positional diffing — compare by index. Element 0 in the left array is compared with element 0 in the right array. This is correct for arrays where order is meaningful, like a sequence of events. It is wrong for sets or collections where order is irrelevant.
  • Identity-based diffing — match elements across the two arrays by a unique key (an id field, for example), then compare matched pairs. This is more useful for arrays of objects, but requires knowing which field is the identity key.

For most practical purposes — comparing an API response before and after a change, or reviewing a config update — positional diffing is what a visual tool gives you, and it is usually what you want.

Comparing in code

For automated comparison in tests or pipelines, deep equality checks are the usual approach. In JavaScript, JSON.stringify(a) === JSON.stringify(b) works only if key order is consistent — unreliable for general use. Instead, use a library like deep-equal, Lodash's _.isEqual, or a testing framework's built-in deep equality matcher (expect(a).toEqual(b) in Jest/Vitest).

For generating a structured diff in code, libraries like json-diff, deep-diff, or jsondiffpatch produce machine-readable diff objects that you can process programmatically — useful for logging what changed between two API responses or two database records.

When to use a visual diff tool

A side-by-side visual diff is most useful for human review: checking a config change before deploying, inspecting what an API response changed between environments, or reviewing a data migration. The four-panel layout — raw left, normalised left, normalised right, raw right — lets you see both what changed and how the two documents were normalised before comparison.