โ† Back to Blog

How to Compare Two JSON Files

2026-04-17 ยท 5 min read

โ† Back to Blog

How to Compare Two JSON Files

ยท 5 min read

Core Challenges of JSON Comparison

Comparing JSON files is more complex than comparing plain text files because JSON semantic equivalence is not the same as string equivalence. Two JSON objects with different field ordering may be completely semantically equivalent ({"a":1,"b":2} and {"b":2,"a":1} are the same JSON object). But if compared with a plain text diff tool, they'd be considered different โ€” producing many false positive "differences."

Similarly, JSON with different formatting (indentation, line breaks, spacing) differs at the text level but is semantically identical. This is why you need JSON-aware diff tools rather than simply using the diff command on two files. Understanding this distinction is the prerequisite for choosing the right comparison method.

Command-Line Comparison with jq

jq is the most powerful tool for command-line JSON comparison. Combined with jq -S (sort keys) and the standard diff command, you can achieve semantic comparison that ignores field ordering:

# ๅŸบๆœฌ่ฏญไน‰ๆฏ”่พƒ๏ผˆๅฟฝ็•ฅๅญ—ๆฎต้กบๅบๅ’Œๆ ผๅผๅทฎๅผ‚๏ผ‰
# Semantic comparison (ignores field order and formatting differences)
diff /dev/null; then
  echo "JSONs are identical"
else
  echo "JSONs differ"
fi

Deep JSON Comparison in JavaScript

In JavaScript/Node.js, using === directly to compare two objects only compares references, not content. Deep comparison is needed to confirm whether two JSON values are semantically equivalent:

// ไฝฟ็”จ JSON.stringify + ๆŽ’ๅบ้”ฎ๏ผˆ็ฎ€ๅ•ๅœบๆ™ฏ๏ผ‰
// Using JSON.stringify + sorted keys (simple scenarios)
function jsonEqual(a, b) {
  return JSON.stringify(sortKeys(a)) === JSON.stringify(sortKeys(b));
}

function sortKeys(obj) {
  if (Array.isArray(obj)) return obj.map(sortKeys);
  if (obj && typeof obj === 'object') {
    return Object.fromEntries(
      Object.entries(obj).sort().map(([k, v]) => [k, sortKeys(v)])
    );
  }
  return obj;
}

// ไฝฟ็”จ fast-deep-equal ๅบ“๏ผˆๆŽจ่๏ผŒๅค„็†่พน็ผ˜ๆƒ…ๅ†ต๏ผ‰
// Using fast-deep-equal library (recommended, handles edge cases)
import equal from 'fast-deep-equal';
console.log(equal(obj1, obj2)); // true/false

// ไฝฟ็”จ deep-diff ๅบ“่Žทๅ–่ฏฆ็ป†ๅทฎๅผ‚
// Using deep-diff library for detailed differences
import { diff } from 'deep-diff';
const differences = diff(obj1, obj2);
// differences ๆ˜ฏๅ˜ๆ›ดๆ•ฐ็ป„๏ผŒๆฏไธชๅ…ƒ็ด ๅŒ…ๅซ่ทฏๅพ„ใ€็ฑปๅž‹ใ€ๆ—งๅ€ผใ€ๆ–ฐๅ€ผ

JSON Comparison in Python

Python's JSON comparison is very concise because Python's dictionary comparison naturally ignores key ordering:

import json

# ๅŠ ่ฝฝๅนถๆฏ”่พƒ๏ผˆPython dict ๆฏ”่พƒๅฟฝ็•ฅ้”ฎ้กบๅบ๏ผ‰
# Load and compare (Python dict comparison ignores key order)
with open('file1.json') as f:
    obj1 = json.load(f)
with open('file2.json') as f:
    obj2 = json.load(f)

print(obj1 == obj2)  # True if semantically equal

# ่Žทๅ–่ฏฆ็ป†ๅทฎๅผ‚
# Get detailed differences
def json_diff(obj1, obj2, path=""):
    if type(obj1) != type(obj2):
        print(f"{path}: type changed {type(obj1).__name__} โ†’ {type(obj2).__name__}")
        return
    if isinstance(obj1, dict):
        for key in set(obj1) | set(obj2):
            if key not in obj1:
                print(f"{path}.{key}: added = {obj2[key]}")
            elif key not in obj2:
                print(f"{path}.{key}: removed = {obj1[key]}")
            else:
                json_diff(obj1[key], obj2[key], f"{path}.{key}")
    elif isinstance(obj1, list):
        for i, (a, b) in enumerate(zip(obj1, obj2)):
            json_diff(a, b, f"{path}[{i}]")
        if len(obj1) != len(obj2):
            print(f"{path}: length changed {len(obj1)} โ†’ {len(obj2)}")
    elif obj1 != obj2:
        print(f"{path}: {obj1!r} โ†’ {obj2!r}")

API Response Comparison: Usage in Testing

In API testing, comparing response JSON is a common requirement: verifying that interface behavior is consistent before and after refactoring, or comparing responses across different environments (staging/production). Usually you need to ignore dynamic fields (like timestamps, request IDs) before comparing:

# ไฝฟ็”จ jq ๅฟฝ็•ฅๅŠจๆ€ๅญ—ๆฎตๅŽๆฏ”่พƒไธคไธช API ๅ“ๅบ”ๅฟซ็…ง
# Using jq to ignore dynamic fields before comparing two API response snapshots
IGNORE_FIELDS='del(.meta.timestamp, .meta.requestId, .data.updatedAt)'

diff \
   /tmp/old.json
git show HEAD:config.json | jq -S . > /tmp/new.json
diff /tmp/old.json /tmp/new.json

JSON Patch: Standard Format for Describing JSON Differences

RFC 6902 defines the JSON Patch format for describing a sequence of operations (add, remove, replace, move, copy, test) to apply to a JSON document. This is not just a standard way to represent "differences" โ€” it can also serve as the API's PATCH request format, precisely describing modifications the client wants to make to a resource:

In scenarios requiring precise tracking and replay of JSON change history (like collaborative editing, configuration change auditing), JSON Patch is more useful than simple "before and after comparison": you can apply a series of Patch operations step by step for fine-grained version control and change rollback. Combined with RFC 7396's JSON Merge Patch (a simpler partial update format), most API PATCH operation needs can be met.

// JSON Patch ็คบไพ‹ / JSON Patch example
[
  { "op": "replace", "path": "/user/name", "value": "Bob" },
  { "op": "add", "path": "/user/phone", "value": "+1-555-0100" },
  { "op": "remove", "path": "/user/legacy_field" }
]

// ไฝฟ็”จ fast-json-patch ๅบ“ๅบ”็”จ Patch
import jsonpatch from 'fast-json-patch';
const patched = jsonpatch.applyPatch(originalDoc, patchOps).newDocument;

// ็”Ÿๆˆไธคไธชๅฏน่ฑกไน‹้—ด็š„ Patch
const patch = jsonpatch.compare(originalDoc, modifiedDoc);

Try the online tool now โ€” no installation, completely free.

Open Tool โ†’

Try the free tool now

Use Free Tool โ†’