Confrontare due strutture JSON

confronta due strutture JSON ed elenca le aggiunte, le rimozioni e le modifiche, chiave per chiave

Perché confrontare due JSON?

Confrontare due strutture JSON ricorre regolarmente nella vita di uno sviluppatore. Una risposta API che cambia dopo un aggiornamento. Un file di configurazione che diverge tra due ambienti. Un export di oggetti che deve allinearsi a un riferimento. Un diff JSON risponde precisamente a questa domanda: cosa è stato aggiunto, rimosso o modificato, e dove?

Un diff riga per riga (alla git diff) spesso non basta. Se la formattazione differisce (spazi, ordine delle chiavi), il diff testuale segnala centinaia di righe mentre la struttura di dati è identica. Un comparatore JSON lavora sulla struttura una volta parsata, il che elimina questo rumore e rivela solo gli scarti semantici.

Il formato del diff prodotto dallo strumento

Per ogni scarto, lo strumento restituisce:

  • un percorso in formato JSONPath semplificato, per esempio $.user.address[0].city;
  • un tipo tra added (aggiunto a destra), removed (presente solo a sinistra), modified (valori diversi);
  • il valore di sinistra e/o il valore di destra a seconda del tipo.

Un diff vuoto significa che i due JSON sono strutturalmente identici, indipendentemente dalla loro formattazione o dall'ordine delle chiavi.

Come l'algoritmo percorre le due strutture

L'algoritmo è ricorsivo. A ogni livello, identifica il tipo dei due valori confrontati:

  • Se entrambi sono oggetti associativi, prende l'unione delle chiavi. Per ogni chiave, scende ricorsivamente, o marca added/removed se la chiave esiste solo da un lato.
  • Se entrambi sono array ordinati, confronta posizione per posizione. Una differenza all'inizio dell'array può spostare tutto il resto, il che produce un diff verboso: è un limite assunto del diff strutturale ingenuo.
  • Se i tipi differiscono (oggetto contro array, scalare contro null), è marcato modified.
  • Se i due valori sono scalari (stringa, numero, booleano, null), basta un semplice confronto stretto.

L'ordine delle chiavi in un oggetto non conta: {"a": 1, "b": 2} e {"b": 2, "a": 1} producono un diff vuoto. È conforme alla semantica JSON, dove l'ordine non è significativo. Invece, l'ordine degli elementi di un array conta: un array è ordinato per costruzione.

Un esempio concreto

Ecco due versioni di un oggetto utente:

// gauche
{
  "id": 42,
  "name": "Alice",
  "roles": ["admin", "editor"]
}

// droite
{
  "id": 42,
  "name": "Alice Martin",
  "roles": ["admin", "viewer"],
  "active": true
}

Il diff prodotto:

  • $.name: modified, "Alice""Alice Martin"
  • $.roles[1]: modified, "editor""viewer"
  • $.active: added, true

Casi d'uso

  • Diff di ambienti: confrontare l'output di un endpoint in pre-produzione e in produzione. Molto utile durante una migrazione o un refresh di cache.
  • Audit di migrazione: confrontare un export prima e dopo trasformazione per verificare che nessun campo sia stato perso.
  • Regressione di API: prima e dopo una modifica, confrontare la risposta per una richiesta identica. Un diff vuoto conferma la non regressione.
  • Sincronizzazione di configurazioni: confrontare composer.json tra due branch, due file .eslintrc, due configurazioni Symfony.
  • Snapshot test: sostituire un confronto riga per riga con un confronto strutturale in una suite di test di integrazione.

Limiti del diff strutturale

Confrontare array ordinati è un limite noto dei diff strutturali ingenui. Se si inserisce un elemento all'inizio dell'array, tutte le posizioni successive vengono spostate e il diff segnala ogni scarto come una modifica. Per casi simili, esistono algoritmi più avanzati (Myers, Patience, diff per chiave naturale), ma escono dal campo di uno strumento di confronto generalista.

Il diff non dice nemmeno perché un cambiamento è avvenuto: è una constatazione. Per analizzare una regressione, bisogna incrociare questa constatazione con i commit, i deploy e i parametri della richiesta.

JSON diff vs JSON Patch (RFC 6902)

Un formato complementare è JSON Patch (RFC 6902). Descrive, come operazioni (add, remove, replace, move, copy, test), come trasformare un documento JSON in un altro. Mentre il nostro diff è una constatazione (umana), JSON Patch è una ricetta (macchina). Le due rappresentazioni sono equivalenti per casi semplici, e JSON Patch è utile per API RESTful che accettano modifiche parziali.

Domande frequenti

Il diff dipende dall'ordine delle chiavi?

No: per un oggetto JSON, l'ordine non è significativo. Il comparatore produce lo stesso risultato che le chiavi siano ordinate o no.

Come gestire gli array il cui ordine non è importante?

Lo strumento considera per default gli array come ordinati (è la semantica JSON). Se trattate insiemi, ordinate i due array per una chiave naturale prima di confrontare, o usate un servizio di confronto specializzato che tenga conto di questa semantica.

Qual è la differenza con un diff Git?

Git confronta righe di testo. Se l'indentazione o l'ordine delle chiavi differisce, il diff Git è molto verboso. Il diff JSON lavora sulla struttura parsata e segnala solo gli scarti di dati.

Un JSON invalido è accettato?

No: se uno dei due JSON non fa il parsing, lo strumento restituisce un errore. Validate prima con il nostro validatore JSON.

Esempio di richiesta

curl -X POST https://cdrn.fr/api/v1/tools/json-diff/execute \
  -H "Content-Type: application/json" \
  -d '{"left":"...","right":"..."}'

Schema di input

Campo Tipo Richiesto Predefinito
left text
right text

Endpoint

  • GET https://cdrn.fr/api/v1/tools - elenca tutti gli strumenti disponibili
  • GET https://cdrn.fr/api/v1/tools/json-diff - recupera lo schema di questo strumento
  • POST https://cdrn.fr/api/v1/tools/json-diff/execute - esegue questo strumento con un payload JSON