Come confrontare due file JSON e trovare le differenze

Il modo più rapido per confrontare due file JSON è incollarli entrambi in uno strumento di diff affiancato, formattarli allo stesso modo e leggere le righe evidenziate. La parte difficile di solito non è il confronto. È il rumore: chiavi riordinate, rientri diversi e virgole finali sparse possono far sembrare che due file quasi identici non abbiano nulla in comune.

Questa guida spiega come ottenere un diff pulito e affidabile. Vedremo perché i file JSON sembrano divergere sulla carta rimanendo identici nel significato, i pochi metodi che vale la pena conoscere e un esempio pratico da seguire. Se si vuole solo lo strumento, la nostra pagina di confronto JSON fa tutto questo nel browser.

Perché i file JSON sono ingannevolmente difficili da confrontare

JSON ha una grammatica piccola e rigorosa (vedi la specifica su json.org), ma lascia molta libertà nella disposizione del testo. Due file possono descrivere lo stesso oggetto e differire byte per byte. Un semplice diff di testo non sa nulla di tutto ciò, quindi lo segnala tutto diligentemente.

Ecco la cosa fondamentale da interiorizzare prima di iniziare: le chiavi degli oggetti in JSON non hanno ordine. La specifica (RFC 8259) definisce un oggetto come un insieme non ordinato di coppie nome/valore. Quindi {"name":"Ada","id":7} e {"id":7,"name":"Ada"} sono uguali, anche se un diff per righe li colorerà di rosso e verde.

Sembra una modifica, di solito non lo è
Cosa si vede nel diffÈ una vera modifica?Cosa fare
Chiavi in ordine diversoNo, gli oggetti non sono ordinatiOrdinare le chiavi su entrambi i lati
Rientro a 2 spazi vs 4 spaziNoFormattare entrambi i lati allo stesso modo
Minimizzato vs formattatoNoFormattare entrambi i lati
Riga vuota finale alla fine del fileNoIgnorare o rimuovere gli spazi bianchi
Un valore è cambiato da "7" a 7Sì, stringa vs numeroIndagare, è reale
Elementi dell'array in ordine diversoForse, gli array sono ordinatiDecidere se l'ordine è rilevante qui

L'ultima riga sorprende molti. Gli array mantengono il loro ordine, gli oggetti no. Quindi [1, 2, 3] e [3, 2, 1] sono genuinamente diversi, ma le chiavi all'interno di un oggetto possono essere mescolate liberamente. Per i dettagli su come JavaScript analizza tutto questo, MDN ha un ottimo riferimento sull'oggetto JSON.

Quattro modi per confrontare JSON e quando usarne ciascuno

Non esiste un metodo unico migliore. Dipende da dove si trovano i file e cosa si vuole scoprire. Ecco come si confrontano le opzioni più comuni.

MetodoIdeale perSforzoCapisce JSON?
Esame visivoFile piccoli, uno o due campiBassoNo, sei tu il parser
Strumento diff onlineControlli rapidi, incollare da qualsiasi fonteBassoCon formato + ordinamento chiavi, sì
Riga di comando (jq, diff)File su disco, scripting, file di grandi dimensioniMedioSì, ordinando prima
IDE o git diffFile già in un repositoryBasso se committatiBasato sulle righe per impostazione predefinita

Per la maggior parte delle persone uno strumento browser vince in velocità perché non c'è nulla da installare e si può incollare uno snippet direttamente da un log o da una chiamata API. Il problema è il rumore di formattazione, di cui ci occuperemo subito dopo. Chi lavora molto nel terminale dovrebbe imparare jq, e mostreremo il flag che conta.

Il confronto pulito più rapido, passo dopo passo

Questa è la procedura che uso quando qualcuno mi dà due file di configurazione e chiede "cosa è diverso?" Richiede circa quindici secondi.

  1. Aprire lo strumento di confronto JSON.
  2. Incollare l'originale a sinistra, la nuova versione a destra.
  3. Fare clic su Formatta su entrambi i lati in modo che condividano lo stesso rientro.
  4. Attivare Ordina chiavi (canonicalizza) in modo che le chiavi riordinate smettano di apparire come modifiche.
  5. Leggere il risultato. Il verde indica aggiunto, il rosso indica rimosso, e un valore modificato appare come uno di ciascuno.

I passi tre e quattro sono il trucco vero. Una volta che entrambi i file sono formattati in modo identico e le loro chiavi sono ordinate, l'unica cosa rimasta da evidenziare è ciò che è effettivamente cambiato. Il nostro motore di diff è costruito su diff-match-patch di Google, che confronta prima riga per riga così rimane veloce anche su file lunghi.

Un esempio pratico

Supponiamo che si stia esaminando una modifica a un record utente. Ecco com'era prima:

{
  "name": "Ada Lovelace",
  "role": "editor",
  "active": true,
  "seats": 3
}

E qui è il dopo, come lo ha consegnato un collega:

{
  "active": true,
  "name": "Ada Lovelace",
  "role": "admin",
  "seats": 5,
  "team": "platform"
}

Se si inseriscono in un semplice diff per righe sembra che quasi ogni riga si sia spostata, perché le chiavi sono in ordine diverso. Formattando e ordinando entrambi, la storia vera è breve:

Cosa è cambiato davvero
CampoPrimaDopoModifica
roleeditoradminModificato
seats35Modificato
teamplatformAggiunto
nameAda LovelaceAda LovelaceNessuna modifica
activetruetrueNessuna modifica (solo spostato)

Tre vere modifiche: un cambio di ruolo, un conteggio dei posti e un nuovo campo team. Il riordinamento era rumore. Quella promozione da editor ad admin è esattamente il tipo di cosa che si vuole individuare in revisione, ed è facile perderla quando è sepolta sotto venti righe di falsi positivi.

Eliminare il rumore di formattazione dalla riga di comando

Se i file sono già su disco, la stessa idea di "formatta e ordina" funziona con due brevi comandi. Il flag -S dice a jq di ordinare le chiavi degli oggetti; inviare tramite pipe normalizza entrambi i file in modo che un semplice diff sia onesto:

jq -S . old.json > old.sorted.json
jq -S . new.json > new.sorted.json
diff old.sorted.json new.sorted.json

Ora diff riporta solo i valori che sono veramente cambiati, perché entrambi i file hanno lo stesso rientro e lo stesso ordine delle chiavi. Questo è l'equivalente nel terminale di fare clic su Formatta e ordina chiavi nel browser.

Diff di testo vs diff strutturale

Tutto quanto sopra è un diff di testo: veloce, visivo e perfetto per una persona che legge una modifica. Un diff strutturale va oltre e descrive la modifica come dati. Lo standard per questo è JSON Patch, definito in RFC 6902, che esprime le modifiche come operazioni come replace e add a un percorso dato. Si vuole un diff strutturale quando un programma deve applicare la modifica, non solo una persona che la esamina visivamente. Per la revisione quotidiana, un diff di testo con chiavi ordinate è più che sufficiente.

Insidie comuni da tenere d'occhio

InsidiaPerché colpisceSoluzione
Precisione dei numeriGli interi sopra 2^53 − 1 perdono precisione in JavaScriptConfrontare grandi ID come stringhe; vedi MAX_SAFE_INTEGER
Escape Unicode"café" e "café" sono la stessa stringa, byte diversiFormattare entrambi i lati, il che normalizza la codifica
Chiavi duplicateLa maggior parte dei parser mantiene silenziosamente l'ultimaValidare il JSON prima di fidarsi del diff
Virgole finaliNon JSON valido; un file potrebbe non essere nemmeno parsabileCorreggere prima la sintassi, il validatore lo segnalerà
Stringa vs numero"5" e 5 sembrano simili ma sono tipi diversiÈ una vera modifica, non ignorarla

Strumenti correlati

JSON è raramente l'unico formato con cui si ha a che fare. Se si confrontano configurazioni tra ambienti, il confronto YAML applica la stessa idea a YAML. Esaminare le modifiche tra due chiamate API è esattamente ciò per cui è costruito il diff delle risposte API, e gli aggiornamenti delle dipendenze sono più facili da leggere sulla pagina package.json diff.

Domande frequenti

Il confronto online di file JSON li carica da qualche parte?
Su comparetext.org il diff viene eseguito nel browser. I due file JSON vengono confrontati da JavaScript sul proprio computer, quindi nulla viene inviato a un server a meno che non si faccia esplicitamente clic su Salva o Condividi. Questo lo rende sicuro per file di configurazione, risposte API e altri dati che non si vorrebbero incollare in un sito casuale che carica ad ogni pressione di tasto.
Perché i miei due file JSON mostrano ogni riga come diversa?
Quasi sempre è la formattazione, non modifiche reali. Un file è minimizzato o rientrato con tabulazioni, l'altro con due spazi, oppure le chiavi degli oggetti sono in ordine diverso. Fare clic su Formatta su entrambi i lati in modo che usino lo stesso rientro, poi ordinare le chiavi in modo che l'ordine smetta di importare. Dopo di ciò il diff di solito si riduce alla manciata di valori che sono cambiati davvero.
Come faccio a confrontare JSON ignorando l'ordine delle chiavi?
Le chiavi degli oggetti JSON non hanno un ordine definito, quindi {"a":1,"b":2} e {"b":2,"a":1} sono uguali. Per far concordare un diff di testo, ordinare le chiavi su entrambi i lati prima del confronto. Nel browser, usare l'opzione di canonicalizzazione (ordina chiavi). Nella riga di comando, jq lo fa: jq -S . file.json. Una volta che entrambi i file hanno le chiavi nello stesso ordine ordinato, appariranno solo le vere modifiche ai valori.
Posso confrontare file JSON di grandi dimensioni senza che la pagina si blocchi?
Sì, fino a un certo punto. Un diff in modalità riga rimane veloce su file con migliaia di righe perché confronta prima intere righe invece di ogni singolo carattere. I file molto grandi (diversi megabyte) sono gestiti meglio con uno strumento da riga di comando come jq o git diff, che trasmette i dati in streaming. Per qualsiasi cosa si possa scorrere comodamente in un browser, un diff online è l'opzione più rapida.
Qual è la differenza tra un diff di testo e un diff strutturale di JSON?
Un diff di testo confronta i file riga per riga, nello stesso modo in cui confronterebbe due saggi. Un diff strutturale capisce JSON, quindi sa che una chiave riordinata non è una modifica e che un valore spostato all'interno di un array è uno spostamento, non una cancellazione più un'aggiunta. I diff di testo sono più veloci e sufficienti per la maggior parte delle revisioni. I diff strutturali (ad esempio JSON Patch per RFC 6902) contano quando si deve descrivere una modifica come dati che un programma può applicare.
Come faccio a confrontare due risposte API?
Salvare ogni risposta in un file o copiarla dalla scheda di rete del browser, poi incollare la vecchia risposta a sinistra e quella nuova a destra. Formattare entrambe in modo che il rientro corrisponda, e ordinare le chiavi se l'API non le restituisce in ordine stabile. Lo strumento api-response-diff è ottimizzato esattamente per questo: individuare un campo rinominato, un codice di stato modificato o un valore che è passato da stringa a numero tra due chiamate.

Pronti a provarlo? Incollare i file nello strumento di confronto JSON e vedere cosa è cambiato.