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".
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.
The path exists only on the right: a new key, or a new element past the end of an array.
The path exists only on the left.
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.
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.
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.
# 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 // 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.
# 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) == {} 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.
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.
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.
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.
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.
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.
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.
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.