Javascript is required
TroubleshootingPublished 2026-07-286 min read

Why JSON Key Order Changes a Diff: Structural Comparison

Two JSON texts can differ only by object-member order while representing the same data model for a given comparison policy. This article covers object-key order, array order, and structural comparison. It does not cover Myers sequence diff.

By: Tiny
JSON DiffObject KeysCanonicalizationComparison

1. Problem: text equality is not structural equality

{"a":1,"b":2} and {"b":2,"a":1} have different bytes, so direct string equality fails. Parse and compare structure only when the domain treats object members as a mapping; do not discard order when raw text is signed, cached, or displayed as meaningful output.

2. Minimal reproduction: sort objects, not arrays

A recursive key sort makes the two objects stable, but arrays stay ordered, so [1,2] and [2,1] remain different.

const left = '{"b":2,"a":1}';
const right = '{"a":1,"b":2}';
console.log(left === right); // false
console.log(JSON.stringify({ a: 1, b: 2 }) === JSON.stringify({ b: 2, a: 1 })); // false
console.log([1, 2].toString() === [2, 1].toString()); // false

3. Root cause: JSON text keeps order; policy supplies meaning

A parser reads members in text order and JavaScript has property-enumeration rules, but neither is a business comparison policy. A structural JSON diff normally treats objects as name-to-value mappings and arrays as ordered sequences. Duplicate member names are not a stable basis for interoperable comparison because parsers can handle them differently.

4. Recommended policy: canonicalize only plain JSON objects

For acyclic JSON values, recursively sort object keys and recurse into array elements without reordering them. Compare the resulting canonical text or create a path diff from it. This is unsuitable when insertion order is a product semantic, and it does not replace domain rules for numbers or dates.

5. Complete code: stable JSON representation

This TypeScript runs in browsers and Node.js. It rejects non-finite numbers so JSON.stringify cannot silently turn them into null.

type Json = null | boolean | number | string | Json[] | { [key: string]: Json };
function canonicalize(value: Json): Json {
  if (typeof value === "number" && !Number.isFinite(value)) throw new TypeError("JSON numbers must be finite");
  if (value === null || typeof value !== "object") return value;
  if (Array.isArray(value)) return value.map(canonicalize);
  const result: { [key: string]: Json } = {};
  for (const key of Object.keys(value).sort()) result[key] = canonicalize(value[key]);
  return result;
}
function sameJsonObjectShape(leftText: string, rightText: string): boolean {
  return JSON.stringify(canonicalize(JSON.parse(leftText) as Json)) === JSON.stringify(canonicalize(JSON.parse(rightText) as Json));
}
console.log(sameJsonObjectShape('{"b":2,"a":1}', '{"a":1,"b":2}')); // true
console.log(sameJsonObjectShape('{"items":[1,2]}', '{"items":[2,1]}')); // false

6. Incorrect approaches

Sorting characters corrupts strings and escapes. Sorting arrays hides list-order changes unless the domain explicitly defines the array as a set. JSON.stringify(JSON.parse(text)) alone also preserves insertion order rather than producing a common key order.

7. Boundaries: numbers, null, and duplicate names

JavaScript parses 1 and 1.0 to the same Number; compare text or tokens if spelling matters. Null is different from a missing field. Reject duplicate object names, report them, or define an explicit parser policy before comparison.

8. Verification

Test reordered keys, nested reordered keys, reordered arrays, null versus missing, and alternate number spellings. The first two should match, while the rest follow the documented domain policy. Do not apply this rule blindly to signed raw HTTP text.

9. FAQ

Does sorting rewrite my file? Not here; it produces an in-memory comparison form.

Can an array ignore order? Only when the domain defines it as a set and also defines identity and duplicates.

Can I use a deep-equality library? Yes, after verifying its object and array semantics match the JSON contract.

10. Summary

Key order creates a text difference, not automatically an object change. Canonicalize object keys recursively and preserve array order for an explicit, testable comparison policy. Document array, null, and raw-text requirements before relying on it.

Sources and further reading

Specifications and references for checking the article’s technical details.

Related articles

Keep exploring

Use the related browser tool to test the workflow with your own sample data.

Open related tool