alltools.one
JSON
2025-06-24
7 min
alltools.one Team
JSONDiffDebuggingAPITesting

JSON Diffデバッグガイド:複雑なデータの変更を発見する

標準的なテキストdiffツールはJSONに対して力不足です。キーの並び替え、空白の変更、深いネストにより、実際のデータ変更を覆い隠すノイズの多いdiffが生成されます。構造的JSON diffはテキスト表現ではなく、セマンティクス — 実際のデータ — を比較します。このガイドでは、効果的なJSON比較とデバッグの技術を紹介します。

標準的なDiffがJSONに失敗する理由

意味的に同一の2つのJSONオブジェクトを考えてみましょう:

バージョンA:

{"name":"Alice","age":30,"roles":["admin","editor"]}

バージョンB:

{
  "age": 30,
  "name": "Alice",
  "roles": ["admin", "editor"]
}

テキストdiffはすべての行が変更されたと表示しますが、データは同一です。構造的diffは同一であることを認識します。

実際の変更を考えてみましょう:

// 変更前
{ "user": { "name": "Alice", "permissions": ["read", "write", "admin"] } }

// 変更後
{ "user": { "name": "Alice", "permissions": ["read", "write"] } }

構造的diffは removed user.permissions[2]: "admin" と報告します — まさに必要な情報です。

JSON Diffツールで即座にお試しください。

JSON変更の種類

構造的JSON diffは変更を以下のように分類します:

変更タイプ
追加新しいキーまたは配列要素
削除欠落したキーまたは配列要素
変更既存キーの値が変更
型変更値の型が変更(string → number)
移動配列要素の並び替え

コマンドラインJSON Diff

jqによるクイック比較

# キーをソートして比較
diff <(jq -S . before.json) <(jq -S . after.json)

# 特定のパスを比較
diff <(jq '.config.database' before.json) <(jq '.config.database' after.json)

専用ツール

# json-diff (npm)
npx json-diff before.json after.json

# 出力:
# {
#   "user": {
#     "permissions": [
#       "read",
#       "write",
#-      "admin"
#     ]
#   }
# }

# jd (Go)
jd before.json after.json

APIレスポンスの変更をデバッグ

APIレスポンスが予期せず変更された場合、体系的な比較が根本原因の特定に役立ちます:

ステップ1:ベースラインのキャプチャ

既知の正常なレスポンスを保存:

curl -s https://api.example.com/users/123 | jq -S . > baseline.json

ステップ2:現在のキャプチャ

curl -s https://api.example.com/users/123 | jq -S . > current.json

ステップ3:構造的Diff

# 人間が読める形式
npx json-diff baseline.json current.json

# 機械が読める形式(JSON Patchフォーマット)
npx json-diff baseline.json current.json --json

ステップ4:ノイズのフィルタリング

リクエストごとに変わるフィールド(タイムスタンプ、リクエストID)を除外:

# 比較前に揮発性フィールドを削除
jq 'del(.meta.requestId, .meta.timestamp)' response.json

JSON Patch(RFC 6902)

JSON PatchはJSONドキュメントへの変更を記述するための標準化されたフォーマットです:

[
  { "op": "replace", "path": "/user/name", "value": "Bob" },
  { "op": "remove", "path": "/user/permissions/2" },
  { "op": "add", "path": "/user/email", "value": "bob@example.com" }
]

操作:

  • add: 新しい値を追加
  • remove: 値を削除
  • replace: 既存の値を変更
  • move: 値を新しいパスに移動
  • copy: 値を新しいパスにコピー
  • test: 値が存在するか確認(条件付きパッチ用)

JSON Patchはドキュメント全体を置換する代わりにAPIに増分更新を送信するのに便利です。

設定のドリフト検出

設定ファイルの経時変化を追跡:

#!/bin/bash
# drift-check.sh
BASELINE="config-baseline.json"
CURRENT="config-current.json"

DIFF=$(npx json-diff "$BASELINE" "$CURRENT" 2>/dev/null)

if [ -n "$DIFF" ]; then
  echo "Configuration drift detected:"
  echo "$DIFF"
  exit 1
fi

echo "No drift detected."

テストでのJSON Diff

テストで構造的比較を使用してAPIレスポンスを検証:

const { diff } = require('json-diff');

test('API response matches expected structure', async () => {
  const response = await fetch('/api/users/123');
  const data = await response.json();
  
  const expected = {
    id: 123,
    name: 'Alice',
    role: 'admin'
  };
  
  const changes = diff(expected, data);
  expect(changes).toBeUndefined(); // undefinedは同一を意味
});

大きなJSONファイルの処理

数メガバイトのJSONファイルでは、ビジュアルdiffは非実用的になります:

  1. 先にクエリ: diffの前にJSONPathで関連セクションを抽出。JSONPathガイドを参照。
  2. サマリーモード: すべての変更を表示するのではなく、タイプ別に変更数をカウント
  3. ストリーミングdiff: jdのようなツールはストリーミングで大きなファイルを効率的に処理
# 特定のセクションを抽出して比較
jq '.data.users[:10]' large-before.json > section-before.json
jq '.data.users[:10]' large-after.json > section-after.json
npx json-diff section-before.json section-after.json

FAQ

JSON PatchとJSON Merge Patchのどちらを使うべきですか?

JSON Patch(RFC 6902)は操作の配列を使用し、配列要素の操作を含むあらゆる変更を表現できます。JSON Merge Patch(RFC 7396)はよりシンプルで、部分的なJSONオブジェクトを送信してターゲットとマージします。シンプルなオブジェクト更新にはMerge Patchを、配列操作やアトミック操作が必要な場合はJSON Patchを使用してください。

キーの順序が異なるJSONをdiffするにはどうすればよいですか?

ほとんどの構造的JSON diffツールはキーの順序を無視します — {"a":1,"b":2}{"b":2,"a":1} と等しいです。テキストベースのツールでは、キーをアルファベット順にソートする jq -S . で先に正規化します。JSON Diffツールはキーの順序を自動的に処理します。

関連リソース

Published on 2025-06-24
JSON Diff Debugging Guide: Find Changes in Complex Data | alltools.one