Come confrontare due file XML e vedere cosa è cambiato
Il modo più veloce per confrontare due file XML è incollarli entrambi in uno strumento di diff affiancato, formattarli allo stesso modo e leggere le righe che evidenzia. Il confronto è la parte facile. È il rumore a confondere le persone: attributi riordinati, spazi tra i tag e prefissi di namespace possono far sembrare due file che significano la stessa cosa come se non avessero nulla in comune.
Questa guida spiega come ottenere un diff XML pulito e affidabile. Vedremo perché due documenti equivalenti si allontanano sulla carta, quali metodi vale la pena conoscere, e un esempio pratico da seguire. Se vuoi solo lo strumento, la nostra pagina di confronto XML fa tutto questo nel browser.
Perché i file XML sono ingannevolmente difficili da confrontare
XML ha una grammatica rigorosa (vedi la specifica XML del W3C), ma lascia agli autori molta libertà su come disporre il testo. Due documenti possono descrivere esattamente gli stessi dati e differire byte per byte. Un diff di testo semplice non capisce nulla di tutto ciò, quindi segnala tutto.
Ecco il fatto chiave da tenere a mente: in XML, l'ordine degli attributi di un
elemento non è significativo. L'
XML Information Set
tratta gli attributi come un insieme non ordinato. Quindi
<user id="7" role="admin"/> e
<user role="admin" id="7"/> contengono la stessa informazione,
anche se un diff di righe li dipinge di rosso e verde. L'ordine degli elementi,
invece, di solito conta.
| Cosa vedi nel diff | È una modifica reale? | Cosa fare |
|---|---|---|
| Attributi in ordine diverso | No, l'ordine degli attributi non è significativo | Canonicalizza entrambi i lati |
| Indentazione di 2 vs 4 spazi | No | Formatta entrambi i lati allo stesso modo |
| Spazi tra gli elementi | Di solito no | Formatta, o rimuovi gli spazi insignificanti |
<br/> vs <br></br> | No, stesso elemento vuoto | Canonicalizza entrambi i lati |
| Un prefisso di namespace diverso per lo stesso URI | No, i prefissi sono etichette arbitrarie | Confronta per URI del namespace, non per prefisso |
| Elementi figli in ordine diverso | Di solito sì, l'ordine degli elementi conta | Indaga, è probabilmente reale |
Quell'ultima riga è quella da tenere d'occhio. L'ordine degli attributi è libero, ma l'ordine degli elementi figli fa parte del documento nella maggior parte degli schemi. Se vuoi il dettaglio su come un parser vede tutto questo, MDN ha un solido riferimento sull'analisi di XML con DOMParser.
Quattro modi per confrontare XML e quando usare ciascuno
Non esiste un unico metodo migliore. Dipende da dove si trovano i file e da cosa stai cercando di scoprire. Ecco come si confrontano le opzioni comuni.
| Metodo | Ideale per | Sforzo | Capisce l'XML? |
|---|---|---|---|
| Controllo visivo | File minuscoli, uno o due elementi | Basso | No, il parser sei tu |
| Strumento di diff online | Controlli rapidi, incollare da qualsiasi posto | Basso | Con la formattazione, sì |
Riga di comando (xmllint) | File su disco, scripting, forma canonica | Medio | Sì, con --c14n |
IDE o git diff | File già in un repository | Basso se committato | Basato su righe per impostazione predefinita |
Per la maggior parte delle persone uno strumento da browser vince in velocità:
niente da installare, e puoi incollare un frammento direttamente da un file di
configurazione o da una risposta SOAP. Il problema è il rumore di formattazione,
di cui ci occupiamo dopo. Se vivi nel terminale,
xmllint di libxml2
è lo strumento da conoscere.
Il confronto pulito più veloce, passo dopo passo
Questa è la routine che uso quando qualcuno mi passa due file di configurazione e chiede "cosa c'è di diverso?". Richiede circa quindici secondi.
- Apri lo strumento di confronto XML.
- Incolla l'originale a sinistra, la nuova versione a destra.
- Clicca su Formatta su entrambi i lati così da condividere la stessa indentazione.
- Cerca le differenze reali. Il verde è aggiunto, il rosso è rimosso, e un valore modificato appare come uno di ciascuno.
- Ignora le righe che sono solo riordino di attributi o spazi.
Il passo tre è gran parte del trucco. Una volta che entrambi i documenti usano la stessa indentazione, l'unica cosa che resta da evidenziare è ciò che è davvero cambiato. Il nostro motore di diff è costruito sul diff-match-patch di Google, che confronta prima riga per riga per restare veloce anche su file lunghi.
Un esempio pratico
Supponi di revisionare una modifica a una configurazione di servizio. Ecco il prima:
<user id="7" role="editor">
<name>Ada Lovelace</name>
<active>true</active>
<seats>3</seats>
</user>
Ed ecco il dopo, così come te lo ha passato un collega:
<user role="admin" id="7">
<name>Ada Lovelace</name>
<active>true</active>
<seats>5</seats>
<team>platform</team>
</user>
Mettili in un diff di righe grezzo e la primissima riga sembra modificata, perché
id e role si sono scambiati di posto. Formatta entrambi,
confronta per significato, e la storia reale è breve:
| Nodo | Prima | Dopo | Modifica |
|---|---|---|---|
@role | editor | admin | Modificato |
seats | 3 | 5 | Modificato |
team | — | platform | Aggiunto |
@id | 7 | 7 | Nessuna modifica (solo spostato) |
name | Ada Lovelace | Ada Lovelace | Nessuna modifica |
Tre modifiche reali: una promozione di ruolo, un conteggio di posti e un nuovo
elemento team. Lo scambio di attributi era rumore. Quella promozione da
editor ad admin è esattamente il tipo di cosa che vuoi
cogliere in revisione, ed è facile da perdere quando è sepolta sotto una riga che
il diff ha segnalato per errore.
XML canonico: il modo corretto di ignorare il rumore
Formattare entrambi i lati gestisce l'indentazione, ma esiste uno standard pensato esattamente per questo problema. L'XML canonico, definito dal W3C in Canonical XML 1.1, riscrive un documento in un'unica forma normalizzata: attributi ordinati, elementi vuoti espansi, spazi nei tag normalizzati e attributi predefiniti resi espliciti. Due documenti equivalenti producono un output canonico identico. È l'equivalente XML dell'ordinamento delle chiavi JSON.
xmllint --c14n old.xml > old.c14n.xml
xmllint --c14n new.xml > new.c14n.xml
diff old.c14n.xml new.c14n.xml
Ora diff segnala solo i contenuti che sono davvero cambiati, perché
entrambi i file sono stati normalizzati allo stesso modo. Se vuoi solo
un'indentazione leggibile invece della forma canonica rigorosa,
xmllint --format file.xml la formatta, che è l'equivalente da terminale
del clic su Formatta nel browser.
Namespace: la parte che confonde tutti
I namespace XML permettono a due documenti di usare lo stesso vocabolario con
etichette di prefisso diverse. <ns1:user> associato a un URI e
<u:user> associato allo stesso URI sono lo stesso
elemento; il prefisso è solo un soprannome locale. Un diff di testo vede
ns1 contro u e segnala una modifica che non c'è. La
soluzione è confrontare per URI del namespace invece che per prefisso, che è
esattamente ciò che fa la canonicalizzazione. La specifica
Namespaces in XML
è il riferimento se devi risolvere una discussione al riguardo.
Insidie comuni da tenere d'occhio
| Insidia | Perché morde | Soluzione |
|---|---|---|
| Codifica dei caratteri | Un file UTF-8 e uno UTF-16 possono contenere lo stesso testo ma differire byte per byte | Normalizza la codifica; la dichiarazione XML la indica |
| Riferimenti a entità | & e un & letterale possono apparire entrambi per lo stesso carattere | Canonicalizza, il che risolve le entità in modo coerente |
| CDATA vs testo con escape | <![CDATA[a<b]]> e a<b sono lo stesso contenuto testuale | Confronta il valore analizzato, non i byte grezzi |
| Spazi significativi | Dentro xml:space="preserve", gli spazi contano e non vanno rimossi | Non tagliare alla cieca; rispetta xml:space |
| Tag auto-chiudenti | <x/> e <x></x> sono identici | Canonicalizza così che entrambi vengano resi allo stesso modo |
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 in termini dell'albero XML: questo attributo è cambiato, quell'elemento figlio è stato inserito in questo percorso. Vuoi un diff strutturale quando un programma deve applicare la modifica o quando l'ordine degli elementi davvero non conta e vuoi ignorarlo. Per la revisione quotidiana, un diff di testo di due documenti formattati basta e avanza.
Strumenti correlati
L'XML è raramente l'unico formato con cui hai a che fare. Se stai confrontando payload di API, confrontare JSON applica la stessa idea al JSON. Le pagine con markup sono più facili da leggere nella pagina di confronto HTML, e le impostazioni di ambiente si allineano bene nello strumento di confronto config.
Domande frequenti
- Confrontare file XML online li carica da qualche parte?
- Su comparetext.org il diff viene eseguito nel tuo browser. I due file XML vengono confrontati da JavaScript sulla tua macchina, quindi nulla viene inviato a un server a meno che tu non clicchi esplicitamente su Salva o Condividi. Questo lo rende sicuro per file di configurazione, messaggi SOAP e altri dati che non vorresti incollare in un sito che carica tutto a ogni battitura.
- Perché i miei due file XML mostrano ogni riga come diversa?
- Quasi sempre è formattazione, non modifiche reali. Un file è minificato o indentato con tabulazioni, l'altro con due spazi, oppure gli attributi sono in un ordine diverso. Clicca su Formatta su entrambi i lati così da usare la stessa indentazione. Dopo di che il diff di solito si riduce alla manciata di valori che sono davvero cambiati. Per una normalizzazione più rigorosa, canonicalizza prima entrambi i file con xmllint --c14n.
- L'ordine degli attributi conta quando si confronta XML?
- No. In XML gli attributi di un elemento sono un insieme non ordinato, quindi
<a x="1" y="2"/>e<a y="2" x="1"/>sono equivalenti. Un diff di testo semplice non lo sa e segnalerà il riordino come una modifica. L'XML canonico ordina gli attributi in un ordine fisso, quindi canonicalizzare entrambi i lati prima di confrontare fa sparire il falso positivo. L'ordine degli elementi, invece, di solito è significativo. - Come confronto XML ignorando i prefissi di namespace?
- I prefissi di namespace sono etichette locali per un URI di namespace, quindi
ns1:usereu:userassociati allo stesso URI sono lo stesso elemento. Per confrontare correttamente, normalizza per URI invece che per prefisso. Il modo più semplice è canonicalizzare entrambi i documenti con xmllint --c14n, che riscrive le associazioni di namespace in modo coerente, e poi fare il diff dei risultati. Un diff di testo grezzo non può farlo da solo. - Posso confrontare file XML grandi senza che la pagina si blocchi?
- Sì, fino a un certo punto. Un diff in modalità riga resta veloce su file con migliaia di righe perché confronta prima righe intere invece di ogni carattere. I file molto grandi (diversi megabyte) sono gestiti meglio con uno strumento da riga di comando come xmllint o git diff, che fa streaming dei dati. Per tutto ciò che puoi scorrere comodamente in un browser, un diff online è l'opzione più veloce.
- Qual è la differenza tra un diff di testo e un diff strutturale di XML?
- Un diff di testo confronta i file riga per riga, allo stesso modo in cui confronterebbe due temi. Un diff strutturale capisce l'albero XML, quindi sa che un attributo riordinato non è una modifica e può segnalare un elemento inserito tramite il suo percorso. I diff di testo sono più veloci e sufficienti per la maggior parte delle revisioni una volta formattati entrambi i lati. I diff strutturali contano quando un programma deve applicare la modifica o quando vuoi ignorare l'ordine degli elementi.
Pronto a provarlo? Incolla i tuoi file nello strumento di confronto XML e guarda cosa è cambiato.