Quando un'API HTTP deve aggiornare parte di una risorsa, due standard IETF si contendono il lavoro: JSON Merge Patch (RFC 7396) e JSON Patch (RFC 6902). Sembrano simili ma funzionano in modo molto diverso —— Merge Patch sovrappone un oggetto parziale, mentre JSON Patch invia un elenco esplicito di operazioni. Questa guida mostra come funziona ciascuno, li confronta testa a testa e ti aiuta a scegliere quello giusto.
Il problema: aggiornamenti parziali
Una richiesta PUT sostituisce un'intera risorsa, il che è sprecone e rischioso quando vuoi cambiare solo un campo. Il metodo HTTP PATCH esiste per aggiornamenti parziali —— ma PATCH non definisce un formato di body. È quello che questi due standard forniscono.
JSON Merge Patch (RFC 7396)
Un Merge Patch è semplicemente una versione parziale del documento di destinazione. Il server lo sovrappone: le key nel patch vengono impostate, le key messe a null vengono cancellate, e le key non menzionate restano intatte.
// Originale
{ "title": "Hello", "author": "Ada", "tags": ["a", "b"], "draft": true }
// Merge Patch body (Content-Type: application/merge-patch+json)
{ "title": "Hello World", "draft": null }
// Risultato
{ "title": "Hello World", "author": "Ada", "tags": ["a", "b"] }
// ^ title aggiornato ^ author intatto ^ draft cancellato (era null) È intuitivo e compatto. La fregatura: poiché null significa «cancella», non puoi usare Merge Patch per impostare un valore a null, e gli array si possono solo sostituire interamente —— non c'è modo di cambiare un singolo elemento dell'array.
JSON Patch (RFC 6902)
Un JSON Patch è un array ordinato di operazioni, ognuna che mira a una posizione con un path JSON Pointer. È esplicito e preciso:
// JSON Patch body (Content-Type: application/json-patch+json)
[
{ "op": "replace", "path": "/title", "value": "Hello World" },
{ "op": "remove", "path": "/draft" },
{ "op": "add", "path": "/tags/-", "value": "c" },
{ "op": "test", "path": "/author", "value": "Ada" }
]Le sei operazioni:
| op | Effetto |
|---|---|
add | Aggiungere un valore (o append a un array con /-) |
remove | Cancellare il valore a un path |
replace | Sostituire il valore a un path |
move | Spostare un valore da un path a un altro |
copy | Copiare un valore in un altro path |
test | Asserire un valore (aborta l'intero patch se fallisce) |
L'op test abilita aggiornamenti sicuri e atomici: se il documento non è nello stato atteso, l'intero patch viene rifiutato —— utile per controllo di concorrenza ottimistico.
Confronto testa a testa
| JSON Merge Patch | JSON Patch | |
|---|---|---|
| RFC | 7396 | 6902 |
| Content-Type | application/merge-patch+json | application/json-patch+json |
| Leggibilità | Alta —— sembra i dati | Minore —— elenco di operazioni |
| Può impostare un valore a null | No (null significa cancellare) | Sì |
| Modificare un singolo elemento di array | No (sostituire l'intero array) | Sì (per indice) |
| Aggiornamenti condizionali / atomici | No | Sì (op test) |
| Move / copy | No | Sì |
| Ideale per | Semplici aggiornamenti di campi di oggetto | Modifiche precise, consce degli array, atomiche |
Quando usare cosa
- JSON Merge Patch —— quando i client aggiornano per lo più campi di oggetto di primo livello e dai valore a un body che si legge come la risorsa. Ottimo per impostazioni semplici e aggiornamenti di profilo. Solo ricorda che non puoi impostare campi a
nullné modificare item di array. - JSON Patch —— quando devi modificare elementi di array, spostare o copiare valori, impostare campi a
null, o applicare cambiamenti atomicamente con una precondizione. La scelta standard per editing collaborativo e change set amici dell'audit.
Semantica HTTP PATCH: idempotenza e header
Due dettagli HTTP che spesso fregano i team che mettono in produzione un endpoint PATCH:
- PATCH non deve essere idempotente (a differenza di PUT). Un JSON Patch con
{"op":"add","path":"/items/-","value":...}fa append —— applicarlo due volte aggiunge due volte. Se vuoi retry sicuri, o usa un'optestper asserire prima lo stato, o progetta le tue op così che riapplicarle sia no-op (usareplacesu un path fisso invece diaddsu/items/-). - Usa il Content-Type giusto. RFC 6902 =
application/json-patch+json; RFC 7396 =application/merge-patch+json. I server di solito si ramificano su questo. - Concorrenza ottimistica. Combina un'op
testdi JSON Patch (o un headerIf-Matchcon ETag) così writer concorrenti non possono sovrascriversi silenziosamente.
Librerie da conoscere
Per JSON Patch, fast-json-patch è il pacchetto npm di fatto —— applica, genera (diff di due documenti per produrre un patch) e osserva (traccia cambiamenti a un oggetto osservato). Per JSON Merge Patch, le regole sono abbastanza piccole da scriverle inline, ma json-merge-patch le implementa con i casi limite della cancellazione tramite null gestiti.
Applicare patch nel codice
// JavaScript —— fast-json-patch implementa RFC 6902
import { applyPatch } from 'fast-json-patch';
const updated = applyPatch(document, patchOps).newDocument;
// JSON Merge Patch è banale da applicare per ricorsione, ma una libreria
// (es. json-merge-patch) gestisce correttamente le regole di cancellazione tramite null.Dopo aver applicato un patch, puoi confermare che la modifica è esattamente quella che intendevi facendo diff di prima e dopo —— vedi Come confrontare due file JSON o incolla entrambi in JSON Diff.
Domande frequenti
Qual è la differenza tra JSON Patch e JSON Merge Patch?
JSON Merge Patch (RFC 7396) è una copia parziale del documento dove null cancella una key. JSON Patch (RFC 6902) è un array esplicito di operazioni (add, remove, replace, move, copy, test) che mirano a path JSON Pointer. Merge Patch è più semplice; JSON Patch è più potente.
Perché JSON Merge Patch non può impostare un campo a null?
Perché in Merge Patch null è il segnale per cancellare una key. Se devi memorizzare un valore null vero, usa JSON Patch con un'op replace.
Quale Content-Type devo usare?
application/merge-patch+json per Merge Patch e application/json-patch+json per JSON Patch, entrambi inviati con il metodo HTTP PATCH.
Come modifico un elemento di un array?
Usa JSON Patch con un path come /items/2, o fai append con /items/-. Merge Patch può solo sostituire l'intero array.
Strumenti & letture correlate
- JSON Diff —— verifica che un patch abbia prodotto esattamente il cambiamento che ti aspettavi
- Come confrontare due file JSON —— gli algoritmi dietro un diff semantico
- RFC 7396: JSON Merge Patch
- RFC 6901 & 6902: JSON Pointer e Patch