Try the JSON Diff

Why Two Identical JSON Objects Look Different in a Diff — Normalization, Canonical Form, and the Array Problem

Two JSON objects with identical data can fail equality checks because of different key ordering — a false difference that corrupts any text-level diff. Here's the three normalization problems (key ordering, formatting, numeric representation), RFC 8785 canonical JSON for digital signatures, the practical parse-sort-serialize workflow for normalization before diffing, and why arrays create a harder normalization problem with no universal solution.

June 23, 2026 6 min read
Share: Facebook WhatsApp LinkedIn Email
Why Two Identical JSON Objects Look Different in a Diff — Normalization, Canonical Form, and the Array Problem

Two JSON objects that contain the same data — the same keys and the same values — can fail a naive equality check because one has keys in alphabetical order and the other has them in the order they were inserted, and any diff tool that compares raw text will report them as different

The previous articles on this site covered why key order doesn't matter in JSON, practical diff workflows, structural diff fundamentals, JSON Patch, and snapshot testing. This article addresses JSON normalization before diffing — the specific pre-processing steps that make structural comparison meaningful rather than noise-generating.


The three normalization problems that corrupt diffs

Before two JSON documents can be meaningfully compared, they often need to be normalized — transformed into a canonical form that eliminates irrelevant differences. Three categories of "false differences" appear most frequently:

1. Key ordering: JSON objects are unordered by definition (the spec doesn't guarantee order), but serializers typically produce keys in a specific order — insertion order (JavaScript, Python 3.7+), alphabetical order (some libraries), or serialization-order (whatever the code produced them in). The same logical object serialized by two different code paths may have keys in different orders.

2. Formatting: a "minified" JSON string and a "pretty-printed" equivalent are semantically identical. Any text-level diff will report every line as changed.

3. Numeric representation: 1.0 and 1 and 1.00 are the same number in JSON (all represent the integer 1 with possible decimal places). Some serializers produce 1.0; others produce 1. A text diff reports a difference; a structural diff sees the same value.


Canonical JSON: a formal approach to normalization

RFC 8785 defines "JSON Canonicalization Scheme" (JCS) — a specification for producing a canonical representation of JSON where:

  • Keys are sorted lexicographically (Unicode code point order)
  • Whitespace is removed (minified)
  • Numbers are represented in a specific way (no trailing zeros, no unnecessary decimals)
  • Unicode characters are represented consistently

Producing canonical JSON before diffing ensures that two JSON documents representing the same data produce identical strings — making text-level diff tools work correctly on them. Two structurally-identical JSON documents, canonicalized, become byte-identical strings.

When canonical form matters beyond diffing: digital signatures over JSON data require canonical form — if two parties sign the "same" JSON document but with different key orders, their signatures will differ. JCS enables consistent signing of JSON.


Practical normalization workflow

For most development contexts, the normalization steps are simpler than JCS:

  1. Parse: convert the JSON string to a native data structure (object, dict, map)
  2. Sort keys recursively: sort all object keys alphabetically at all nesting levels
  3. Serialize: convert back to JSON with consistent formatting (usually minified, or pretty-printed with consistent indentation)
  4. Diff the normalized outputs

In JavaScript:

function canonicalize(obj) {
  if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {
    return obj;
  }
  return Object.fromEntries(
    Object.keys(obj).sort().map(k => [k, canonicalize(obj[k])])
  );
}

const normalized1 = JSON.stringify(canonicalize(JSON.parse(json1)), null, 2);
const normalized2 = JSON.stringify(canonicalize(JSON.parse(json2)), null, 2);
// Now diff normalized1 vs normalized2

In Python:

import json

def normalize(obj):
    if isinstance(obj, dict):
        return {k: normalize(v) for k, v in sorted(obj.items())}
    elif isinstance(obj, list):
        return [normalize(i) for i in obj]
    return obj

normalized1 = json.dumps(normalize(json.loads(json1)), indent=2)
normalized2 = json.dumps(normalize(json.loads(json2)), indent=2)

Arrays: the normalization problem that's harder to solve

Key sorting handles the object ordering problem. Arrays are different — array element order is semantically significant in JSON. [1, 2, 3] and [3, 2, 1] are different values, not the same value in a different order.

This creates a genuine diffing challenge: if you have two JSON arrays representing "the same set of items" but in different orders (perhaps the list of authorized users was rebuilt and the database returns them in a different order), a structural diff will show every element as changed, even though the actual change was only to ordering.

No universal solution exists — because JSON doesn't distinguish between "ordered sequences where order matters" (where array order changes are real changes) and "unordered sets that happen to be represented as arrays" (where order changes are noise).

Domain-specific solutions: if you know a specific array should be treated as an unordered set, sort its elements before diffing (for arrays of strings/numbers) or sort by a stable key field (for arrays of objects with IDs). This pre-processing is necessarily domain-specific.


The diff-then-act pattern: using normalized diff for automation

A practical automation pattern: run a normalized diff on two versions of a configuration or API response, and if the diff is non-empty, trigger an alert or action.

Example: a service that fetches configuration from an external API every hour. If the configuration changes, it should restart. Using normalized JSON diff rather than string comparison ensures:

  • Reformatted responses don't trigger false restarts
  • Reordered keys don't trigger false restarts
  • Actual value changes do trigger restarts
def config_changed(old_config, new_config):
    old_normalized = json.dumps(normalize(old_config), sort_keys=True)
    new_normalized = json.dumps(normalize(new_config), sort_keys=True)
    return old_normalized != new_normalized

How to use the JSON Diff tool on sadiqbd.com

  1. For key-ordering false diffs: the tool's structural comparison already handles key ordering — inserting JSON with differently-ordered keys on both sides should show no differences if the values are the same
  2. For formatting differences: pasting minified JSON on one side and pretty-printed on the other should show no structural differences (only formatting changes, which the structural diff ignores)
  3. For array ordering issues: the tool compares arrays positionally by default; if you need set-comparison (order doesn't matter), sort both arrays before pasting

Frequently Asked Questions

Does the order of JSON keys affect performance in any real application? Rarely in practice, but yes in theory. Some JSON parsers that build hash maps may have slightly different performance characteristics depending on insertion order (depending on hash table implementation). More practically: some poorly-implemented API clients or parsing code may assume a specific key order (accessing response.body.charAt(2) instead of response.body — treating JSON as text), which breaks when the serializer changes key order. The robust approach is always to use a proper JSON parser and access values by key name, making key order entirely irrelevant to correctness.

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 and see only the structural differences, ignoring formatting and key order.

Share: Facebook WhatsApp LinkedIn Email

JSON Diff

Free, instant results — no sign-up required.

Open JSON Diff →
Similar Tools
Color Converter JSON Unescape & Cleaner Number Base Converter URL Encoder/Decoder Base64 Encoder/Decoder Bcrypt Generator REST API Checker JWT Decoder
Practical JSON Diff Workflows: Debugging APIs, Finding Config Drift, and Verifying Migrations
Developer
Practical JSON Diff Workflows: Debugging APIs, Finding Config Drift, and Verifying Migrations
JSON Structural Diff Fundamentals: Why Key Order Doesn't Matter, Null Isn't "Missing," and Arrays Can Cascade
Developer
JSON Structural Diff Fundamentals: Why Key Order Doesn't Matter, Null Isn't "Missing," and Arrays Can Cascade
JSON Patch and JSON Pointer: How "What Changed" Becomes a Standardized, Executable Sequence of Operations
Developer
JSON Patch and JSON Pointer: How "What Changed" Becomes a Standardized, Executable Sequence of Operations
Snapshot Testing Is Just JSON Diffing With Extra Steps — Here's Why That Explains Both Its Strengths and Its Noise
Developer
Snapshot Testing Is Just JSON Diffing With Extra Steps — Here's Why That Explains Both Its Strengths and Its Noise
Using JSON Diff to Detect API Breaking Changes — Schema Diff, Merge Patch, and Contract Testing
Developer
Using JSON Diff to Detect API Breaking Changes — Schema Diff, Merge Patch, and Contract Testing