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.

Ser ut som en endring, men er det som regel ikke
Hva du ser i diffenEr det en reell endring?Hva du skal gjøre
Attributter i en annen rekkefølgeNei, attributtrekkefølge er ikke betydningsfullKanoniser begge sider
2-mellomroms vs 4-mellomroms innrykkNeiFormater begge likt
Mellomrom mellom elementerSom regel ikkeFormater, eller fjern ubetydelige mellomrom
<br/> vs <br></br>Nei, samme tomme elementKanoniser begge sider
Et annet navneromsprefiks for samme URINei, prefikser er vilkårlige etiketterSammenlign etter navnerom-URI, ikke prefiks
Barneelementer i en annen rekkefølgeSom regel ja, elementrekkefølge betyr noeUndersø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.

MetodeBest forInnsatsForstår XML?
Å lese det gjennom selvBittesmå filer, ett eller to elementerLavNei, du er parseren
Online diff-verktøyRaske sjekker, innliming fra hvor som helstLavMed formatering, ja
Kommandolinje (xmllint)Filer på disk, skripting, kanonisk formMiddelsJa, med --c14n
IDE eller git diffFiler som allerede er i et repoLav ved commitLinjebasert 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.

  1. Åpne XML-sammenligningsverktøyet.
  2. Lim inn originalen til venstre, den nye versjonen til høyre.
  3. Klikk på Formater på begge sider så de bruker samme innrykk.
  4. Søk etter reelle forskjeller. Grønt er lagt til, rødt er fjernet, og en endret verdi vises som en av hver.
  5. 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:

Hva som faktisk ble endret
NodeFørEtterEndring
@roleeditoradminEndret
seats35Endret
teamplatformLagt til
@id77Ingen endring (bare flyttet)
nameAda LovelaceAda LovelaceIngen 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

FallgruveHvorfor den biterLøsning
TegnkodingEn UTF-8- og en UTF-16-fil kan inneholde samme tekst, men være forskjellige byte for byteNormaliser kodingen; XML-deklarasjonen angir den
Entitetsreferanser&amp; og et bokstavelig & kan begge opptre for samme tegnKanoniser, som løser opp entiteter konsekvent
CDATA vs escapet tekst<![CDATA[a<b]]> og a&lt;b er det samme tekstinnholdetSammenlign den parsede verdien, ikke de rå bytene
Betydningsfulle mellomromInni xml:space="preserve" betyr mellomrom noe og må ikke fjernesIkke trim blindt; respekter xml:space
Selvlukkende tagger<x/> og <x></x> er identiskeKanoniser 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:user og u:user bundet 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.