{ }jsonkitOpen the tool

How to Compare Two JSON Files (JSON Diff)

8 min read · updated 2026-07-12

Comparing two JSON files sounds like it should be as simple as running a text diff. In practice, a line-based diff lies to you constantly: it flags reordered keys as changes, drowns real differences in whitespace noise, and misses values that are semantically identical but formatted differently. What you actually want is a *structural* diff that understands objects, arrays, and value types.

This guide explains why plain-text diffing fails for JSON, what a structural diff computes, and how to run one using an online tool, git diff on normalized files, jq, and standardized formats like JSON Patch and JSON Merge Patch. Every technique here works with client-side or command-line tools, so your data never has to leave your machine.

Why a Plain-Text Diff Is Wrong for JSON

Tools like diff, git diff, and most editor comparison views work on lines of text. They know nothing about JSON's structure — to them a file is a sequence of characters split on newlines. That mismatch produces three recurring problems.

Key order is insignificant in JSON, but not to a text diff

The JSON specification defines an object as an *unordered* collection of name/value pairs. These two objects are semantically identical:

Same data, different key order — a text diff flags every linejsonc
// file A
{ "name": "Ada", "role": "admin", "active": true }

// file B
{ "active": true, "name": "Ada", "role": "admin" }

A structural comparison reports *no differences* here. A line diff reports the entire object as changed because the lines don't line up. Serializers, ORMs, and language Map/dict implementations emit keys in different orders all the time, so this is not a rare edge case.

Formatting and whitespace create phantom changes

Indentation depth, 2- vs 4-space indent, \n vs \r\n line endings, and minified vs beautified output all change the text without changing the data. One file run through a JSON formatter and another left minified will diff as almost entirely different despite carrying identical data.

Semantically equal values look different

1.0, 1, and 1e0 are the same JSON number, and the strings "a" and "a" are the same JSON string — both decode to the character *a*. A text diff sees different characters; a value-aware diff parses each side and sees equal values.

What a Structural (Semantic) JSON Diff Computes

A structural diff parses both documents into trees, then walks them in parallel, comparing by key (for objects) and by index (for arrays). Instead of line changes it produces a list of differences, each anchored to a path and classified into one of three categories.

CategoryMeaningExample
AddedA key or element exists in the right document but not the left/user/phone appears in B
RemovedA key or element exists in the left but not the right/user/fax gone in B
ChangedA key exists in both, but the value differs/user/role: "editor" → "admin"

Paths are typically written as JSON Pointer (RFC 6901), e.g. /items/0/price. Because the diff is keyed on structure, reordering an object's keys produces zero differences and reformatting produces zero differences. That is the whole point: the output contains only changes that actually alter the data.

Comparing JSON Files with an Online Diff Tool

The fastest way to get a readable structural diff is a dedicated tool. Paste the two documents side by side; it parses, normalizes, and compares them, highlighting added, removed, and changed values at each path — with no manual sorting or reformatting.

±JSON Diff / CompareOpen the tool

A good browser-based diff runs entirely client-side, so the JSON you paste never leaves the page. That matters when you're comparing API responses, config files, or fixtures that may contain tokens, internal IDs, or customer data. Reach for it when you want a quick visual answer to "what actually changed?" without wiring up a script.

Comparing JSON with git diff on Normalized Files

When JSON lives in a repository, you often want the diff to survive as a reviewable change. The trick is to *normalize before you diff* so git's line-based engine only sees real differences. Normalizing means sorting object keys and using consistent indentation.

Normalize both files, then diff thembash
# Sort keys and pretty-print with 2-space indent (requires jq)
jq -S . old.json > old.norm.json
jq -S . new.json > new.norm.json

# Now a line diff reflects real structural changes
git diff --no-index old.norm.json new.norm.json

The -S / --sort-keys flag is what makes this reliable: jq recursively sorts every object's keys, so reordered inputs collapse to identical output. For files already committed to a repo, you can automate this with a git textconv diff driver that pipes JSON through jq -S . before diffing, so git diff always compares normalized text.

Comparing and Querying JSON with jq

jq is the Swiss-army knife for command-line JSON. Beyond normalization, it can answer targeted comparison questions directly. Suppose you only care whether a specific field changed:

Compare a single field, then diff top-level key setsbash
# Are the "version" values equal?
test "$(jq -r .version old.json)" = "$(jq -r .version new.json)" \
  && echo "same" || echo "changed"

# Show which top-level keys were added or removed
jq -n --slurpfile a old.json --slurpfile b new.json '
  ($a[0] | keys) as $ak | ($b[0] | keys) as $bk
  | { added: ($bk - $ak), removed: ($ak - $bk) }'

The second command loads both files, extracts their top-level key sets, and uses jq's array-subtraction operator to compute additions and removals — a minimal structural diff of the object's shape. For deeper, path-level comparisons across large documents, reach for a purpose-built diff tool rather than hand-rolling recursion in jq. If you write a lot of these expressions, the JSONPath / JQ tester lets you iterate on queries interactively.

When to Ignore Array Order

Objects are unordered, but JSON arrays *are* ordered — position is part of the value. A diff should respect that by default, comparing element 0 to element 0, element 1 to element 1, and so on. But some arrays model an unordered set, and there ordering is an implementation detail you want to ignore.

Order matters

  • A list of steps, events, or timeline entries where sequence is meaningful
  • Paginated or ranked results where position encodes rank
  • Coordinates or tuples like [lng, lat] where index has meaning

Order is incidental

  • A set of tags, roles, or permission strings
  • A collection of objects each identified by a stable id, returned in arbitrary order
  • Enum-like value lists where duplicates and position don't matter

For the incidental case, a naive index-by-index diff reports a mountain of false changes when the same elements simply moved. Better strategies: sort both arrays by a stable key before comparing, or match elements by an id field rather than by position. Some diff tools let you configure an array-matching key so [{id:1},{id:2}] and [{id:2},{id:1}] compare as equal.

Sort an array of objects by id before comparing (jq)bash
jq -S '.items |= sort_by(.id)' old.json > old.norm.json
jq -S '.items |= sort_by(.id)' new.json > new.norm.json
git diff --no-index old.norm.json new.norm.json

Representing a Diff: JSON Patch and JSON Merge Patch

Once you've computed a difference, you often need to *transmit or apply* it — for example, to send only what changed in a PATCH request or to store a compact changelog. Two IETF standards formalize this.

JSON Patch (RFC 6902)

JSON Patch is an ordered array of operations (add, remove, replace, move, copy, test), each targeting a JSON Pointer path. It maps almost directly onto the added/removed/changed model of a structural diff.

A JSON Patch document describing three changesjson
[
  { "op": "replace", "path": "/user/role", "value": "admin" },
  { "op": "remove",  "path": "/user/fax" },
  { "op": "add",     "path": "/user/phone", "value": "+1-555-0100" }
]

JSON Merge Patch (RFC 7386)

JSON Merge Patch is simpler and more intuitive: it looks like the target document, where present keys are set and a null value means "delete this key." It cannot express array-element edits or distinguish "set to null" from "delete," but it's compact for object-shaped updates.

The same intent as a Merge Patch — null means deletejson
{
  "user": {
    "role": "admin",
    "fax": null,
    "phone": "+1-555-0100"
  }
}
AspectJSON Patch (6902)JSON Merge Patch (7386)
ShapeArray of operationsLooks like the document
Array editsYes (by index)No (arrays replaced wholesale)
Delete a keyremove opSet value to null
Set a value to nullreplace with nullNot possible
Best forPrecise, scriptable changesSimple object merges / PATCH bodies

Rule of thumb: use JSON Patch when you need precise control, array edits, or an audit trail of operations; use Merge Patch when you're sending a partial object update to an API and null-means-delete is acceptable.

Frequently asked questions

Why does my JSON diff show changes when the data is the same?

Almost always because you used a text/line diff and the two files differ only in key order, indentation, or whitespace. Normalize both files first — sort keys and use consistent formatting (e.g. jq -S .) — or use a structural diff tool that compares by key and value rather than by line.

How do I compare two JSON files on the command line?

Normalize both with jq -S . file.json to sort keys and pretty-print, then run git diff --no-index old.norm.json new.norm.json. jq also lets you compare specific fields or compute added/removed keys with array subtraction directly.

Should a JSON diff ignore array order?

By default, no — JSON arrays are ordered, so position is part of the value. Only ignore order for arrays that model an unordered set (tags, id-keyed collections). In those cases, sort by a stable key or match elements by id before comparing.

What's the difference between JSON Patch and JSON Merge Patch?

JSON Patch (RFC 6902) is an array of explicit operations (add, remove, replace, move, copy, test) targeting JSON Pointer paths, and can edit array elements. JSON Merge Patch (RFC 7386) mirrors the document's shape, where a null value deletes a key; it's simpler but can't do array-element edits or set a value to null.

Is it safe to compare sensitive JSON in an online tool?

Only if the tool runs entirely in your browser and doesn't upload your data. A client-side diff processes everything locally, so tokens, IDs, and customer data never leave your machine. When in doubt, prefer local tools like jq and git for confidential payloads.

How do I represent the difference between two JSON documents programmatically?

Compute a structural diff and emit it as a JSON Patch document — an array of operations keyed on JSON Pointer paths. That format can be stored, reviewed, and applied to the original to reproduce the target document. For simple object updates, a JSON Merge Patch is more compact.