format / diff

JSON差分ツール

2つのJSONオブジェクトをキー・パス単位で比較し、追加・削除・変更箇所を色分けして表示します。 ネストしたオブジェクトや配列にも対応。入力データはサーバーに送信されません。

左右のテキストエリアにJSONを入力し、「差分を計算」ボタンを押してください

使い方

01
  1. 左側(before)に変更前のJSONを入力します
  2. 右側(after)に変更後のJSONを入力します
  3. 「差分を計算」ボタンを押すと、キー・パス単位の差分が一覧表示されます
  4. 緑色(+)が追加、赤色(-)が削除、黄色(~)が値の変更です
  5. パス列はドット記法(例: user.address.city)や配列インデックス(例: items[2])で表示されます

実装コード

02

コアロジックは再帰的な深さ優先探索でJSONを走査しています。 オブジェクト・配列・プリミティブ値を型判別しながら差分エントリを生成します。 外部ライブラリは不要なので、そのままコピーしてご利用いただけます。

export type Result<T> = { ok: true; output: T } | { ok: false; error: string }

export type DiffEntry = {
  path: string
  type: 'added' | 'removed' | 'changed' | 'unchanged'
  left?: unknown
  right?: unknown
}

function diffValues(left: unknown, right: unknown, path: string, result: DiffEntry[]): void {
  if (left === right) {
    result.push({ path, type: 'unchanged', left, right })
    return
  }

  if (
    typeof left === 'object' && left !== null &&
    typeof right === 'object' && right !== null &&
    !Array.isArray(left) && !Array.isArray(right)
  ) {
    const leftObj = left as Record<string, unknown>
    const rightObj = right as Record<string, unknown>
    const keys = new Set([...Object.keys(leftObj), ...Object.keys(rightObj)])
    for (const key of keys) {
      const childPath = path ? `${path}.${key}` : key
      if (!(key in leftObj)) {
        result.push({ path: childPath, type: 'added', right: rightObj[key] })
      } else if (!(key in rightObj)) {
        result.push({ path: childPath, type: 'removed', left: leftObj[key] })
      } else {
        diffValues(leftObj[key], rightObj[key], childPath, result)
      }
    }
    return
  }

  if (Array.isArray(left) && Array.isArray(right)) {
    const len = Math.max(left.length, right.length)
    for (let i = 0; i < len; i++) {
      const childPath = `${path}[${i}]`
      if (i >= left.length) {
        result.push({ path: childPath, type: 'added', right: right[i] })
      } else if (i >= right.length) {
        result.push({ path: childPath, type: 'removed', left: left[i] })
      } else {
        diffValues(left[i], right[i], childPath, result)
      }
    }
    return
  }

  result.push({ path, type: 'changed', left, right })
}

export function diffJson(leftStr: string, rightStr: string): Result<DiffEntry[]> {
  try {
    const left = JSON.parse(leftStr)
    const right = JSON.parse(rightStr)
    const result: DiffEntry[] = []
    diffValues(left, right, '', result)
    return { ok: true, output: result }
  } catch (e) {
    return { ok: false, error: `JSONパースエラー: ${(e as Error).message}` }
  }
}

よくある使用例・注意点

03
APIレスポンスの変更確認
バックエンドのAPIレスポンスが仕様変更されたとき、変更前後のレスポンスをここに貼り付けると、追加・削除・変更されたフィールドを一覧で確認できます。
設定ファイルの比較
JSON形式の設定ファイル(package.json、tsconfig.json など)を比較するのに便利です。本番環境と開発環境の設定差異を素早く把握できます。
キー順序の違いは無視
このツールはキーの順序に関係なく、同じパスに同じ値があれば unchanged として扱います。{"a":1,"b":2}{"b":2,"a":1} は差分なしと判定されます。
配列の比較はインデックスベース
配列の比較はインデックス(位置)ベースで行います。配列の要素が並び替えられた場合、全要素が changed として表示される場合があります。配列の順序変化を正確に検出したい場合は、並び替え後に比較することをお勧めします。
プライバシーについて
入力したJSONはブラウザ内のみで処理されます。サーバーには一切送信されないため、認証情報や個人情報を含むJSONも安全に比較できます。

関連ツール

04
JSONフォーマッターJSONパスクエリテキスト差分ツールJSON → TypeScript型生成

ソースコード

05

このツールのソースコード(テストコードを含む)はGitHubで公開しています。 MITライセンスで自由に利用・改変できます。

GitHub でコードを見る →