JSON Patch
JSON ドキュメントの部分更新操作を配列で記述する標準フォーマット (RFC 6902)。6 操作の意味と JSON Pointer のエスケープ、test による楽観的ロック、PUT や JSON Merge Patch との使い分けを解説
JSON Patch とは
JSON Patch (RFC 6902) は、JSON ドキュメントに対する部分更新操作を配列形式で記述する標準フォーマットである。HTTP PATCH メソッドと組み合わせて、リソースの一部だけを更新する REST API を実装する際に使われる。Content-Type は application/json-patch+json を指定する。
PUT がリソース全体の置換であるのに対し、PATCH + JSON Patch は「何をどう変更するか」を明示的に記述する。大きなリソースの一部だけを更新する場合、転送データ量が大幅に削減される。
6 つの操作
操作は add / remove / replace / move / copy / test の 6 種類しかない。パッチ本体は操作オブジェクトの配列で、上から順に適用される。
[
{ "op": "add", "path": "/tags/2", "value": "urgent" },
{ "op": "remove", "path": "/tags/0" },
{ "op": "replace", "path": "/status", "value": "active" },
{ "op": "move", "from": "/old_name", "path": "/new_name" },
{ "op": "copy", "from": "/template", "path": "/config" },
{ "op": "test", "path": "/version", "value": 3 }
]
| 操作 | 説明 | 用途 |
|---|---|---|
| add | 値を追加 (配列の挿入、新フィールドの追加) | 新しいタグの追加 |
| remove | 値を削除 | 不要なフィールドの削除 |
| replace | 値を置換 | ステータスの更新 |
| move | 値を移動 (remove + add) | フィールド名の変更 |
| copy | 値をコピー | テンプレートからの複製 |
| test | 値が期待通りか検証 (楽観的ロック) | 競合検出 |
path に書くのはプロパティ名ではなく JSON Pointer (RFC 6901) である。/tags/2 はルート直下の tags の 3 番目の要素を指す。ここで最初につまずくのはエスケープで、キー名に含まれる ~ は ~0、/ は ~1 へ置き換えなければならない。a/b というキーを更新するパスは /a~1b になる。URL のパスに似ているせいで、キー名をそのまま埋め込んで意図しない階層を指してしまう事故が起きやすい。
操作の細部も仕様が細かく決めている。add は名前の印象と違い、対象が既存のオブジェクトメンバーなら値を置き換える。配列に対する add は指定位置以降の要素を 1 つ後ろへずらす挿入で、位置に要素数より大きい値は書けない。末尾に足したいときは index ではなく - を使う。move は from を path の先祖にできない。自分の子孫へ移動させる指定は仕様違反になる。
test 操作による楽観的ロック
test は 6 操作の中で唯一、文書を書き換えない。更新前に現在の値を検証し、他のクライアントが同時に変更していないことを確かめる前提条件として使う。後述する JSON Merge Patch にはこれに当たる仕組みがないため、検査と更新を 1 リクエストにまとめられる点が JSON Patch を選ぶ主な理由になる。
[
{ "op": "test", "path": "/version", "value": 3 },
{ "op": "replace", "path": "/status", "value": "shipped" },
{ "op": "replace", "path": "/version", "value": 4 }
]
version が 3 でなければ後続の replace は実行されず、パッチ全体が適用されなかったものとして扱われる。この原子性は RFC 6902 が明記しており、途中まで適用された状態は残らない。一方で返す HTTP ステータスは RFC 6902 の規定ではない。PATCH メソッドを定めた RFC 5789 が競合状態の例として 409 Conflict を挙げているため、それに合わせる実装が多いという関係である。読んだときのバージョンを条件に書き込むという考え方は、DynamoDB の条件付き書き込みと同じだ。
test の等価性は論理的な比較として定義されている。まず同じ JSON 型であることが前提で、数値は数値として等しいかを見るため 3 と 3.0 は等しい。配列は要素数と各位置の値が一致する必要があるが、オブジェクトはメンバーの並び順を問わない。整形やシリアライズの違いで検査が落ちることはない。ただし数値の比較は結局その言語の数値表現に委ねられるので、桁数の極端に多い値を条件にするのは避けたほうがよい。
PUT vs PATCH vs JSON Patch
「注文のステータスを shipped にする」という同じ 1 つの変更を 3 通りで書くと、選択の基準が見えてくる。
// PUT: リソース全体を置換 (全フィールドを送信)
PUT /orders/123
{ "id": "123", "status": "shipped", "items": [...], "address": {...}, "total": 5000 }
// PATCH + JSON Merge Patch: 変更フィールドだけ送信 (シンプル)
PATCH /orders/123
Content-Type: application/merge-patch+json
{ "status": "shipped" }
// PATCH + JSON Patch: 操作を明示的に記述 (高機能)
PATCH /orders/123
Content-Type: application/json-patch+json
[{ "op": "replace", "path": "/status", "value": "shipped" }]
| 方式 | 長所 | 短所 |
|---|---|---|
| PUT | シンプル、べき等 | 全フィールド送信が必要 |
| JSON Merge Patch | シンプル、直感的 | 配列操作が苦手、null で削除 |
| JSON Patch | 配列操作、test 操作、高精度 | 記述が冗長、操作によってはべき等でない |
JSON Merge Patch は RFC 7396 が定めた別のフォーマットで、null を「そのキーを削除する」意味に使う。値として null を保存したいデータに使えないという制約はここから来る。べき等性も方式で違う。PUT と JSON Merge Patch は同じリクエストを 2 回投げても結果が変わらないが、JSON Patch は配列への add のように繰り返すと要素が増える操作を書ける。通信エラーでリトライする前提の API なら、test で前提条件を固定して二重適用を防ぐか、べき等な操作だけで組む。
JSON Patch が適するケース
- 大きなリソースの一部だけを更新する API
- 配列の特定要素を操作する必要がある場合
- 楽観的ロック (test 操作) が必要な場合
- 変更履歴を操作単位で記録したい場合
逆に、更新するフィールドが数個で配列を触らないなら JSON Patch は過剰だ。クライアント側でパッチを組み立てるコードと、サーバー側で 6 操作すべてを正しく実装する手間が、削減できる転送量に釣り合わない。JSON Merge Patch で足りる場面のほうが実際には多い。
理論と実装の両面から学ぶなら関連書籍が参考になる。
この記事は役に立ちましたか?
関連用語
関連する記事
スマホ / パソコン入門書の選び方 - シニアの家族に贈る 1 冊の見つけ方
スマホとパソコンの入門書を「贈る側 / 教える側」の視点で選ぶガイド。機種と OS の一致、文字の大きさ、年度の確認という 3 条件と、Android / iPhone / Windows 11 それぞれの定番書、安全対策の 1 冊まで 2026 年 8 月時点の収載書から紹介します。
Git / GitHub 本ガイド - マンガ / GUI / 仕組み理解の 3 つの入口で選ぶ
Git と GitHub を学ぶ本の選び方を「入口の違い」で整理。マンガで概念を掴む本、GUI から入る本、仕組みを腹落ちさせる本、チーム開発の作法を学ぶ本、手元に置くリファレンスまで、2026 年 8 月時点の定番書で独学ルートを解説します。
本に書いてあることが古くても学べることはある
プログラミングの世界は変化が速く、本の内容がすぐ古くなると言われます。でも、古い本からでも学べることはたくさんあります。何が古くなり、何が古くならないのかを解説します。