JSON Diff

Compare two JSON documents by structure instead of by line. Both sides are parsed and compared path by path, so key order is ignored and the report lists only the paths that were added, removed or changed, such as env.LOG_LEVEL: "info" to "debug".

Original JSON
Changed JSON
Differences

1 Structure, not lines

Key order in a JSON object carries no meaning. Reorder the keys and the data is the same, but a text diff marks every moved line. This tool parses both sides with JSON.parse and compares by path, so reordered keys produce no output. In the Dummy Data example, name and version swap places between the two documents and the report does not mention it.

Arrays are different. In JSON an array is ordered, so by default position counts. Tick Ignore array order when the array is an unordered set, such as tags or rows from a database query without an ORDER BY. In that mode each element is compared as a whole, so an element with one changed field shows as one removal and one addition. Keys inside those elements must also be in the same order to count as a match.

2 Reading the report

added

The path exists only on the right: a new key, or a new element past the end of an array.

removed

The path exists only on the left.

changed

Same path, different value. The report shows the old value, an arrow and the new value, and adds both type names when the type changed.

Type changes count

1 and "1" count as a change, marked number to string. This catches the regression where an API starts sending its IDs as quoted strings.

3 When an array diff explodes

Insert one element at the front of a list and every index after it holds a different value, so a positional comparison reports the entire array as changed. The report is correct but does not tell you what happened. Sort records by a stable key before comparing. If the array holds records with an identifier such as id, sort both sides by it first, or tick Ignore array order and read the added and removed lines.

How to use the JSON Diff tool

  1. Paste the original document on the left and the changed one on the right.
  2. Tick Ignore array order to treat arrays as unordered sets. Use it for database rows and leave it off for a list of steps.
  3. Tick Show unchanged to list every path, including the ones that match.
  4. Click Swap Sides to flip the two documents. Copy Report puts a plain-text summary on the clipboard, one +, - or ~ line per difference.

How to diff JSON in code

Command line

# The cheapest useful trick: normalize, then use a normal diff.
# --sort-keys makes key order irrelevant.
diff <(jq --sort-keys . a.json) <(jq --sort-keys . b.json)

# A structural diff with paths
npx json-diff a.json b.json

# Compare a subset only
diff <(jq -S '.spec.template' a.json) <(jq -S '.spec.template' b.json)

# In CI: fail if a fixture drifted from the recorded response
jq -S . expected.json > /tmp/e.json
curl -s "$API" | jq -S . > /tmp/a.json
diff /tmp/e.json /tmp/a.json || exit 1

JavaScript / Node.js

// npm i just-diff
const { diff } = require("just-diff");

console.log(diff(a, b));
// [ { op: "replace", path: ["env", "LOG_LEVEL"], value: "debug" }, ... ]

// RFC 6902 JSON Patch, if you want to send the change rather than show it
// npm i fast-json-patch
const jsonpatch = require("fast-json-patch");
const patch = jsonpatch.compare(a, b);
jsonpatch.applyPatch(a, patch);   // a now equals b

JSON Patch (RFC 6902) is the standard format for this: a list of add, remove and replace operations against paths. Use it when the diff needs to be sent to another service and applied there.

Python

# pip install deepdiff
from deepdiff import DeepDiff
import json

a = json.load(open("a.json"))
b = json.load(open("b.json"))

print(DeepDiff(a, b, ignore_order=True).pretty())

# In a test, this is the assertion you actually want
def test_response_shape(client):
    assert DeepDiff(expected, client.get("/api/user/1").json(),
                    ignore_order=True) == {}

Common errors and how to fix them

The entire array shows as changed
One element was inserted near the front, so every index after it shifted. Sort both sides by a stable key first, or tick Ignore array order.
Two documents that look identical still differ
Check the types. 1 and "1" look alike in many viewers but are different values, and this tool marks the change as number to string.
Reordered keys showed as changes in my own diff
You compared text instead of structure. Normalize both files with jq --sort-keys, or parse both sides and compare the objects.
Floating-point values differ by a tiny amount
0.1 + 0.2 is not 0.3 in any language that uses IEEE 754 floats. Compare with a tolerance, which DeepDiff supports through significant_digits.

Good to know

We recommend using a JSON diff in a contract test. Record a real API response as a fixture, then diff the live response against it in CI. When the upstream team adds a field, you see a harmless addition. When they rename or retype one, the build fails on the day of the change instead of weeks later in production.

Specifications and references

This tool follows the specifications below. Read them when you need the exact normative wording, for example to settle an edge case between two implementations.

Frequently Asked Questions

How is this different from the text diff checker?

It parses both documents and compares them path by path. Reordering the keys in an object changes nothing here, while a text diff would mark every moved line as changed. Array order does count by default, because JSON arrays are ordered.

What do the markers mean?

A plus (added) means the path exists only on the right, a minus (removed) means only on the left, and a tilde (changed) means the same path holds different values. Unchanged paths stay hidden unless you tick Show unchanged.

Are 1 and "1" the same?

No. A number and a string are different types, so the report shows a change marked number to string. This catches the bug where an API starts sending its IDs as quoted strings.

Can it ignore array order?

Yes. Tick Ignore array order for an unordered collection, such as tags or rows from a query without ORDER BY. Leave it off when position carries meaning, such as a list of steps. In this mode each element is matched as a whole, so an element with one changed field shows as one removal and one addition.

Why is the whole array marked as changed?

Inserting one element at the front shifts every index, so every position now holds a different value. When the array is a set of records, sort both sides by a stable key such as id before pasting, or tick Ignore array order.

Related

For plain text or config files, the diff checker compares line by line. Format both sides first with the JSON formatter if you want to read them side by side, and use the JSONPath tester to pull out the part you care about.

More Text & Content Tools

Browse all text & content tools

Copied to clipboard!