Try the JSON Diff

JSON Structural Diff Fundamentals: Why Key Order Doesn't Matter, Null Isn't "Missing," and Arrays Can Cascade

{"a":1,"b":2} and {"b":2,"a":1} represent identical data per the JSON spec β€” but a text-based diff would show them as completely different. Here's how structural diffing handles key-order (unordered per spec), numeric representation differences (1 vs 1.0), the genuine structural difference between null and a missing key, and why array reordering can cascade into multiple "changes" under positional comparison.

By sadiqbd Β· June 19, 2026

Share:
JSON Structural Diff Fundamentals: Why Key Order Doesn't Matter, Null Isn't "Missing," and Arrays Can Cascade

Two JSON documents can be functionally identical and still fail a naive diff β€” because JSON itself doesn't define whether {"a":1,"b":2} and {"b":2,"a":1} are "different," and most programming languages' JSON parsers don't preserve key order anyway

A structural JSON diff tool needs to make decisions about several things that aren't fully specified by the JSON format itself β€” and understanding these decisions explains why "structural" diffing produces different (and generally more useful) results than treating JSON as plain text.


Key order: JSON objects are unordered, but text isn't

The JSON specification describes objects as unordered collections of name/value pairs β€” {"a": 1, "b": 2} and {"b": 2, "a": 1} represent the same data, per the specification β€” order carries no semantic meaning for JSON objects (unlike JSON arrays, where order is explicitly meaningful β€” [1, 2] and [2, 1] are different arrays).

A text-based diff (comparing the raw JSON text character-by-character, or line-by-line) would show these two objects as "different" β€” the characters differ (the keys appear in a different order), even though the data they represent is identical.

A structural diff β€” which parses the JSON into its underlying data representation before comparing β€” correctly treats these as identical, because structurally, an object with a:1, b:2 is the same as an object with b:2, a:1 β€” order of keys isn't part of what's being compared, reflecting the specification's treatment of objects as unordered.


Why "preserve key order" is sometimes requested anyway, despite the spec

Some tools/contexts do preserve and display key order β€” even though it's not semantically meaningful per the JSON spec, order can be useful for humans reading the JSON (a consistent, predictable order β€” e.g., alphabetical, or "in the order the API documentation lists fields" β€” makes visual scanning/comparison easier for humans, even if the underlying data doesn't "care" about order).

Many JSON-formatting tools (including pretty-printers, covered in previous articles) preserve the order keys appear in the input β€” not because order is semantically required, but because changing the order, unprompted, could be surprising/unhelpful for a human reading the formatted output, even if it wouldn't change the data's meaning.

For diffing specifically: a structural diff tool might offer both: a default mode that ignores key-order differences (treating reordered-but-otherwise-identical objects as "no difference") β€” and, optionally, a mode that also flags "keys are present in a different order" as a (typically minor/informational, not "real") difference β€” for contexts where order consistency, for human-readability reasons, is itself something being checked (e.g., "did our code-formatting tool accidentally reorder the keys in this config file, even though the config's meaning is unchanged?" β€” a question where "key order changed" is itself the relevant information, despite not reflecting a data-level difference).


Number representation: 1, 1.0, and 1e0 may or may not be "the same"

JSON's number format allows multiple textual representations of the same numeric value β€” 1, 1.0, 1.00, and 1e0 (scientific notation) all represent the number one β€” but as text, these are obviously different strings.

Whether a structural diff treats 1 and 1.0 as "the same" or "different" depends on how the JSON is parsed:

  • If parsed into a language-native numeric type (e.g., a floating-point number in most languages) β€” 1, 1.0, and 1e0 would typically all parse to the same numeric value (1.0, as a float) β€” and a diff comparing these parsed values would find no difference.
  • If the original textual representation is preserved (some specialized JSON libraries, particularly those concerned with exact-precision numeric handling β€” covered in the previous article on number-formatting-related JSON issues β€” can preserve the original text, e.g., to avoid floating-point-precision loss for very large integers or high-precision decimals) β€” "1" vs "1.0" as preserved text would be different, even though they represent the same numeric value.

For most practical diffing purposes (comparing API responses, configuration files), treating numerically-equal values as "the same," regardless of textual representation (the first, standard-parsing behavior) is generally the more useful default β€” 1 vs 1.0 representing "the same number, written slightly differently" is typically not the kind of difference someone running a diff is looking for (unlike, say, 1 vs 2, which is a genuine value difference).


null vs missing key vs undefined: three different "absences"

JSON has a null value β€” explicitly representing "this key exists, and its value is null" β€” which is different from the key not being present at all.

{"a": 1, "b": null}

vs

{"a": 1}

These are structurally different β€” the first has two keys (a and b, with b's value being null); the second has only one key (a). A structural diff should (and generally does) flag this as a difference β€” "b": null present vs "b" entirely absent are not the same thing, even though some programming-language contexts might, in certain situations, treat "null" and "undefined/missing" similarly (this language-level similarity doesn't change what the JSON documents themselves structurally contain β€” the diff operates on the JSON, not on how some particular programming language might subsequently interpret the parsed result).

(JSON itself has no "undefined" concept β€” undefined is a JavaScript-specific concept, not part of the JSON specification; JSON.stringify in JavaScript, encountering an undefined value, typically omits that key entirely from the output β€” meaning, from a JSON diffing perspective, "a key whose JavaScript value was undefined" and "a key that was never set at all" produce identical JSON β€” the distinction between undefined and "never set" exists at the JavaScript level, but doesn't survive into the JSON representation at all.)


Array element comparison: position-based, by default

For arrays, structural diffs typically compare elements positionally β€” array[0] is compared against array[0], array[1] against array[1], and so on.

If an element is inserted in the middle of an array β€” e.g., [1, 2, 3] becoming [1, 99, 2, 3] (inserting 99 at position 1) β€” a positional diff would show: position 1 changed from 2 to 99, position 2 changed from 3 to 2, and a new position 3 (3) was added β€” even though, conceptually, "one element was inserted, and everything after it shifted" might feel like a more intuitive description of what happened.

Some more-sophisticated diff algorithms (related to the text-diff algorithms discussed in previous articles β€” Myers' algorithm and similar approaches for finding "minimal edit distance") can, for arrays, attempt to identify "insertions"/"deletions" that better reflect intuitive "what changed" descriptions, rather than purely positional comparison β€” but positional comparison remains the simpler, more common default for general-purpose JSON-structural-diff tools, with array-reordering-aware diffing being a more specialized/advanced feature not universally implemented.


How to use the JSON Diff tool on sadiqbd.com

  1. For "is this the same data, regardless of formatting/order" checks: the structural, key-order-insensitive, numerically-aware comparison is generally the most useful default β€” focusing on genuine data differences, not incidental textual/formatting variations
  2. For null vs missing-key distinctions: these are flagged as differences by structural diffing β€” if you're seeing a difference flagged that seems "trivial" (null vs absent), recognize that this is a genuine structural difference in the JSON itself, even if downstream code might treat both cases similarly
  3. For array reordering: be aware that positional comparison may show multiple, cascading "changes" for what's conceptually a single insertion/deletion within an array β€” if the diff output for an array looks more extensive than expected, consider whether an element was inserted/removed mid-array, shifting positions for everything after it

Frequently Asked Questions

If two JSON documents have different whitespace/formatting but represent the same data, will a structural diff show any difference? No β€” whitespace, indentation, and general formatting (covered in the original JSON-formatter article) aren't part of the parsed data structure β€” a structural diff operates on the parsed result, which doesn't retain any information about the original text's whitespace/formatting β€” two documents differing only in formatting (one minified, one pretty-printed, for instance) would show zero structural differences, which is generally the expected, useful behavior (formatting differences are, for most purposes, not "real" differences in the data).

Is the JSON Diff tool free? Yes β€” completely free, no sign-up required.

Try the JSON Diff tool free at sadiqbd.com β€” compare two JSON documents structurally, ignoring key order and formatting differences.

Share:
Try the related tool:
Open JSON Diff

More JSON Diff articles