Sammenlign to XML-filer og se hva som er endret
Den raskeste måten å sammenligne to XML-filer på er å lime inn begge i et side-om-side diff-verktøy, formatere dem likt og lese linjene det fremhever. Selve sammenligningen er den enkle delen. Støyen er det som forvirrer folk: omorganiserte attributter, mellomrom mellom tagger og navneromsprefikser kan få to filer som betyr det samme til å se ut som om de ikke har noe til felles i det hele tatt.
Denne guiden går gjennom hvordan man får en ren og pålitelig XML-diff. Vi ser på hvorfor to ekvivalente dokumenter driver fra hverandre på papiret, hvilke metoder som er verdt å kunne, og et gjennomarbeidet eksempel du kan følge. Vil du bare ha verktøyet, gjør vår XML-sammenligningsside alt dette i nettleseren.
Hvorfor XML-filer er bedragersk vanskelige å sammenligne
XML har en streng grammatikk (se W3Cs XML-spesifikasjon), men gir skribenter stor frihet i hvordan teksten settes opp. To dokumenter kan beskrive nøyaktig de samme dataene og likevel være forskjellige byte for byte. En vanlig tekstdiff forstår ingenting av dette og flagger derfor alt.
Her er det viktigste å holde fast ved: i XML er rekkefølgen på attributtene
på et element ikke betydningsfull. Det
XML Information Set
behandler attributter som en uordnet mengde. Så
<user id="7" role="admin"/> og
<user role="admin" id="7"/> bærer den samme informasjonen,
selv om en linjediff maler dem røde og grønne. Elementrekkefølge, derimot,
betyr som regel noe.
| Hva du ser i diffen | Er det en reell endring? | Hva du skal gjøre |
|---|---|---|
| Attributter i en annen rekkefølge | Nei, attributtrekkefølge er ikke betydningsfull | Kanoniser begge sider |
| 2-mellomroms vs 4-mellomroms innrykk | Nei | Formater begge likt |
| Mellomrom mellom elementer | Som regel ikke | Formater, eller fjern ubetydelige mellomrom |
<br/> vs <br></br> | Nei, samme tomme element | Kanoniser begge sider |
| Et annet navneromsprefiks for samme URI | Nei, prefikser er vilkårlige etiketter | Sammenlign etter navnerom-URI, ikke prefiks |
| Barneelementer i en annen rekkefølge | Som regel ja, elementrekkefølge betyr noe | Undersøk, dette er sannsynligvis reelt |
Den siste raden er den å holde øye med. Attributtrekkefølge er fri, men rekkefølgen på barneelementer er en del av dokumentet i de fleste skjemaer. Vil du ha detaljene om hvordan en parser ser alt dette, har MDN en solid referanse om å parse XML med DOMParser.
Fire måter å sammenligne XML på, og når man velger hver
Det finnes ingen enkelt beste metode. Det avhenger av hvor filene ligger, og hva du prøver å finne ut. Slik står de vanlige alternativene seg.
| Metode | Best for | Innsats | Forstår XML? |
|---|---|---|---|
| Å lese det gjennom selv | Bittesmå filer, ett eller to elementer | Lav | Nei, du er parseren |
| Online diff-verktøy | Raske sjekker, innliming fra hvor som helst | Lav | Med formatering, ja |
Kommandolinje (xmllint) | Filer på disk, skripting, kanonisk form | Middels | Ja, med --c14n |
IDE eller git diff | Filer som allerede er i et repo | Lav ved commit | Linjebasert som standard |
For de fleste vinner et nettleserverktøy på hastighet: ingenting å
installere, og du kan lime inn et utdrag rett fra en konfigurasjonsfil eller
et SOAP-svar. Haken er formateringsstøy, som vi tar tak i nå. Lever du i
terminalen, er xmllint fra
libxml2
verktøyet å kjenne.
Den raskeste rene sammenligningen, steg for steg
Dette er rutinen jeg bruker når noen rekker meg to konfigurasjonsfiler og spør "hva er forskjellen?" Det tar omtrent femten sekunder.
- Åpne XML-sammenligningsverktøyet.
- Lim inn originalen til venstre, den nye versjonen til høyre.
- Klikk på Formater på begge sider så de bruker samme innrykk.
- Søk etter reelle forskjeller. Grønt er lagt til, rødt er fjernet, og en endret verdi vises som en av hver.
- Ignorer radene som bare er attributtomorganisering eller mellomrom.
Steg tre er det meste av trikset. Når begge dokumentene bruker samme innrykk, er det eneste som gjenstår å fremheve hva som faktisk er endret. Vår diff-motor bygger på Googles diff-match-patch, som sammenligner linje for linje først, så den forblir rask selv på lange filer.
Et gjennomarbeidet eksempel
Si at du gjennomgår en endring i en tjenestekonfigurasjon. Her er før:
<user id="7" role="editor">
<name>Ada Lovelace</name>
<active>true</active>
<seats>3</seats>
</user>
Og her er etter, slik en kollega rakte den til deg:
<user role="admin" id="7">
<name>Ada Lovelace</name>
<active>true</active>
<seats>5</seats>
<team>platform</team>
</user>
Sleng dem inn i en rå linjediff, og den aller første linjen ser endret ut,
fordi id og role har byttet plass. Formater begge,
sammenlign etter betydning, og den reelle historien er kort:
| Node | Før | Etter | Endring |
|---|---|---|---|
@role | editor | admin | Endret |
seats | 3 | 5 | Endret |
team | — | platform | Lagt til |
@id | 7 | 7 | Ingen endring (bare flyttet) |
name | Ada Lovelace | Ada Lovelace | Ingen endring |
Tre reelle endringer: en rolleopprykk, et antall plasser og et nytt
team-element. Attributtbyttet var støy. Det opprykket fra
editor til admin er nøyaktig den typen ting du vil
fange i gjennomgangen, og det er lett å overse når det er begravd under en
linje diffen feilaktig flagget.
Kanonisk XML: den rette måten å ignorere støy på
Å formatere begge sider håndterer innrykk, men det finnes en standard bygget nettopp for dette problemet. Kanonisk XML, definert av W3C i Canonical XML 1.1, skriver om et dokument til én normalisert form: attributter sortert, tomme elementer utvidet, mellomrom i tagger normalisert og standardattributter gjort eksplisitte. To ekvivalente dokumenter produserer identisk kanonisk utdata. Det er XML-ekvivalenten til å sortere JSON-nøkler.
xmllint --c14n old.xml > old.c14n.xml
xmllint --c14n new.xml > new.c14n.xml
diff old.c14n.xml new.c14n.xml
Nå rapporterer diff bare innhold som virkelig er endret, fordi
begge filene er normalisert på samme måte. Vil du bare ha lesbart innrykk i
stedet for streng kanonisk form, formaterer
xmllint --format file.xml den, som er terminal-ekvivalenten til
å klikke på Formater i nettleseren.
Navnerom: delen som forvirrer alle
XML-navnerom lar to dokumenter bruke det samme vokabularet med forskjellige
prefiks-etiketter. <ns1:user> bundet til en URI og
<u:user> bundet til samme URI er det samme
elementet; prefikset er bare et lokalt kallenavn. En tekstdiff ser
ns1 mot u og flagger en endring som ikke er en.
Løsningen er å sammenligne etter navnerom-URI fremfor prefikset, som er
nøyaktig hva kanonisering gjør. Spesifikasjonen
Namespaces in XML
er referansen hvis du må avgjøre en diskusjon om det.
Vanlige fallgruver å se opp for
| Fallgruve | Hvorfor den biter | Løsning |
|---|---|---|
| Tegnkoding | En UTF-8- og en UTF-16-fil kan inneholde samme tekst, men være forskjellige byte for byte | Normaliser kodingen; XML-deklarasjonen angir den |
| Entitetsreferanser | & og et bokstavelig & kan begge opptre for samme tegn | Kanoniser, som løser opp entiteter konsekvent |
| CDATA vs escapet tekst | <![CDATA[a<b]]> og a<b er det samme tekstinnholdet | Sammenlign den parsede verdien, ikke de rå bytene |
| Betydningsfulle mellomrom | Inni xml:space="preserve" betyr mellomrom noe og må ikke fjernes | Ikke trim blindt; respekter xml:space |
| Selvlukkende tagger | <x/> og <x></x> er identiske | Kanoniser så begge gjengis likt |
Tekstdiff vs strukturell diff
Alt ovenfor er en tekst-diff: rask, visuell og perfekt for en person som leser en endring. En strukturell diff går lenger og beskriver endringen i form av XML-treet: dette attributtet ble endret, det barneelementet ble satt inn ved denne stien. Du vil ha en strukturell diff når et program må anvende endringen, eller når elementrekkefølge virkelig ikke betyr noe, og du vil ha den ignorert. For daglig gjennomgang er en tekstdiff av to formaterte dokumenter mer enn nok.
Relaterte verktøy
XML er sjelden det eneste formatet du har med å gjøre. Sammenligner du API-payloads, anvender JSON-sammenligning den samme ideen på JSON. Oppmerkede sider er lettere å lese på HTML-sammenligningssiden, og miljøinnstillinger passer godt på config-sammenligningsverktøyet.
Ofte stilte spørsmål
- Laster online-sammenligning av XML-filer dem opp noe sted?
- På comparetext.org kjører diffen i nettleseren din. De to XML-filene sammenlignes av JavaScript på din egen maskin, så ingenting sendes til en server med mindre du uttrykkelig klikker på Lagre eller Del. Det gjør det trygt for konfigurasjonsfiler, SOAP-meldinger og andre data du ikke vil lime inn på et nettsted som laster opp ved hvert tastetrykk.
- Hvorfor vises hver linje som forskjellig mellom mine to XML-filer?
- Nesten alltid er det formatering, ikke reelle endringer. Den ene filen er minifisert eller innrykket med tabulatorer, den andre med to mellomrom, eller attributtene er i en annen rekkefølge. Klikk på Formater på begge sider så de bruker samme innrykk. Etterpå krymper diffen som regel til de få verdiene som virkelig er endret. For strengere normalisering, kanoniser begge filene med xmllint --c14n først.
- Betyr attributtrekkefølge noe når man sammenligner XML?
- Nei. I XML er attributtene på et element en uordnet mengde, så
<a x="1" y="2"/>og<a y="2" x="1"/>er ekvivalente. En vanlig tekstdiff vet ikke dette og flagger omorganiseringen som en endring. Kanonisk XML sorterer attributter i en fast rekkefølge, så når man kanoniserer begge sider før sammenligning, forsvinner den falske alarmen. Elementrekkefølge, derimot, er som regel betydningsfull. - Hvordan sammenligner jeg XML og ignorerer navneromsprefikser?
- Navneromsprefikser er lokale etiketter for en navnerom-URI, så
ns1:userogu:userbundet til samme URI er det samme elementet. For å sammenligne riktig normaliserer du etter URI fremfor prefiks. Den enkleste måten er å kanonisere begge dokumentene med xmllint --c14n, som skriver om navneromsbindinger konsekvent, og deretter diffe resultatene. En rå tekstdiff kan ikke gjøre dette på egen hånd. - Kan jeg sammenligne store XML-filer uten at siden fryser?
- Ja, opp til et visst punkt. En linjebasert diff forblir rask på filer med tusenvis av linjer fordi den sammenligner hele linjer først i stedet for hvert tegn. Svært store filer (flere megabyte) håndteres bedre med et kommandolinjeverktøy som xmllint eller git diff, som strømmer dataene. For alt du komfortabelt kan scrolle gjennom i en nettleser, er en online-diff det raskere alternativet.
- Hva er forskjellen mellom en tekstdiff og en strukturell diff av XML?
- En tekstdiff sammenligner filene linje for linje, på samme måte som den ville sammenlignet to essays. En strukturell diff forstår XML-treet, så den vet at et omorganisert attributt ikke er en endring, og kan rapportere et innsatt element via stien. Tekstdiffer er raskere og gode nok for de fleste gjennomganger når begge sider er formatert. Strukturelle differ betyr noe når et program må anvende endringen, eller når du vil ha elementrekkefølge ignorert.
Klar til å prøve det? Lim inn filene dine i XML-sammenligningsverktøyet og se hva som er endret.