Jämför två XML-filer och se vad som ändrats

Det snabbaste sättet att jämföra två XML-filer är att klistra in båda i ett diff-verktyg sida vid sida, formatera dem på samma sätt och läsa de rader som markeras. Själva jämförandet är den enkla delen. Bruset är det som ställer till det: omordnade attribut, blanksteg mellan taggar och namnrymdsprefix kan få två filer som betyder samma sak att se ut som om de inte har något gemensamt alls.

Den här guiden går igenom hur man får en ren och pålitlig XML-diff. Vi tittar på varför två likvärdiga dokument glider isär på pappret, vilka metoder som är värda att kunna och ett genomarbetat exempel att följa. Vill du bara ha verktyget gör vår XML-jämförelsesida allt detta i webbläsaren.

Varför XML-filer är bedrägligt svåra att jämföra

XML har en strikt grammatik (se W3C:s XML-specifikation), men ger skribenter stor frihet i hur texten läggs ut. Två dokument kan beskriva exakt samma data och ändå skilja sig byte för byte. En vanlig textdiff förstår inget av detta och flaggar därför allt.

Här är det viktigaste att hålla fast vid: i XML är ordningen på attributen i ett element inte betydelsefull. Det XML Information Set behandlar attribut som en oordnad mängd. Så <user id="7" role="admin"/> och <user role="admin" id="7"/> bär samma information, även om en raddiff målar dem röda och gröna. Elementordning, å andra sidan, spelar oftast roll.

Ser ut som en ändring, men är det oftast inte
Vad du ser i diffenÄr det en verklig ändring?Vad du ska göra
Attribut i en annan ordningNej, attributordning är inte betydelsefullKanonisera båda sidor
2-stegs vs 4-stegs indragNejFormatera båda på samma sätt
Blanksteg mellan elementOftast inteFormatera, eller ta bort obetydliga blanksteg
<br/> vs <br></br>Nej, samma tomma elementKanonisera båda sidor
Ett annat namnrymdsprefix för samma URINej, prefix är godtyckliga etiketterJämför efter namnrymds-URI, inte prefix
Barnelement i en annan ordningOftast ja, elementordning spelar rollUndersök, detta är troligen verkligt

Den sista raden är den att hålla koll på. Attributordning är fri, men ordningen på barnelement är en del av dokumentet i de flesta scheman. Vill du ha detaljerna kring hur en parser ser allt detta har MDN en gedigen referens om att tolka XML med DOMParser.

Fyra sätt att jämföra XML, och när man väljer vilket

Det finns ingen enskilt bästa metod. Det beror på var filerna ligger och vad du försöker ta reda på. Så här står sig de vanliga alternativen.

MetodBäst förInsatsFörstår XML?
Att läsa igenom det självPyttesmå filer, ett eller två elementLågNej, du är parsern
Diff-verktyg onlineSnabba kontroller, inklistring var som helstLågMed formatering, ja
Kommandorad (xmllint)Filer på disk, skript, kanonisk formMedelJa, med --c14n
IDE eller git diffFiler som redan finns i ett repoLåg vid commitRadbaserad som standard

För de flesta vinner ett webbläsarverktyg på snabbhet: inget att installera, och du kan klistra in ett utdrag direkt från en konfigurationsfil eller ett SOAP-svar. Haken är formateringsbrus, som vi tar itu med härnäst. Lever du i terminalen är xmllint från libxml2 verktyget att känna till.

Den snabbaste rena jämförelsen, steg för steg

Det här är rutinen jag använder när någon räcker mig två konfigurationsfiler och frågar "vad skiljer?" Det tar ungefär femton sekunder.

  1. Öppna XML-jämförelseverktyget.
  2. Klistra in originalet till vänster, den nya versionen till höger.
  3. Klicka på Formatera på båda sidor så att de använder samma indrag.
  4. Sök efter verkliga skillnader. Grönt är tillagt, rött är borttaget, och ett ändrat värde visas som ett av vardera.
  5. Ignorera raderna som bara är attributomordning eller blanksteg.

Steg tre är det mesta av tricket. När båda dokumenten använder samma indrag är det enda som återstår att markera vad som faktiskt ändrats. Vår diff-motor bygger på Googles diff-match-patch, som jämför rad för rad först så att den förblir snabb även på långa filer.

Ett genomarbetat exempel

Säg att du granskar en ändring i en tjänstekonfiguration. Här är före:

<user id="7" role="editor">
  <name>Ada Lovelace</name>
  <active>true</active>
  <seats>3</seats>
</user>

Och här är efter, som en kollega räckte över den till dig:

<user role="admin" id="7">
  <name>Ada Lovelace</name>
  <active>true</active>
  <seats>5</seats>
  <team>platform</team>
</user>

Släng in dem i en rå raddiff och allra första raden ser ändrad ut, eftersom id och role bytt plats. Formatera båda, jämför efter betydelse, och den verkliga historien är kort:

Vad som faktiskt ändrades
NodFöreEfterÄndring
@roleeditoradminÄndrad
seats35Ändrad
teamplatformTillagd
@id77Ingen ändring (bara flyttad)
nameAda LovelaceAda LovelaceIngen ändring

Tre verkliga ändringar: en rollhöjning, ett antal platser och ett nytt team-element. Attributbytet var brus. Den befordran från editor till admin är precis den sortens sak du vill fånga i granskningen, och den är lätt att missa när den är begravd under en rad som diffen felaktigt flaggat.

Kanonisk XML: det rätta sättet att ignorera brus

Att formatera båda sidor hanterar indrag, men det finns en standard byggd just för det här problemet. Kanonisk XML, definierad av W3C i Canonical XML 1.1, skriver om ett dokument till en enda normaliserad form: attribut sorterade, tomma element expanderade, blanksteg i taggar normaliserade och standardattribut gjorda explicita. Två likvärdiga dokument ger identisk kanonisk utdata. Det är XML-motsvarigheten till att sortera JSON-nycklar.

xmllint --c14n old.xml > old.c14n.xml
xmllint --c14n new.xml > new.c14n.xml
diff old.c14n.xml new.c14n.xml

Nu rapporterar diff bara innehåll som verkligen ändrats, eftersom båda filerna normaliserats på samma sätt. Vill du bara ha läsbart indrag i stället för strikt kanonisk form formaterar xmllint --format file.xml den, vilket är terminalmotsvarigheten till att klicka på Formatera i webbläsaren.

Namnrymder: delen som förvirrar alla

XML-namnrymder låter två dokument använda samma vokabulär med olika prefixetiketter. <ns1:user> bunden till en URI och <u:user> bunden till samma URI är samma element; prefixet är bara ett lokalt smeknamn. En textdiff ser ns1 mot u och flaggar en ändring som inte är en. Lösningen är att jämföra efter namnrymds-URI snarare än prefixet, vilket är precis vad kanonisering gör. Specifikationen Namespaces in XML är referensen om du behöver avgöra ett gräl om det.

Vanliga fallgropar att hålla utkik efter

FallgropVarför den biterLösning
TeckenkodningEn UTF-8- och en UTF-16-fil kan innehålla samma text men skilja sig byte för byteNormalisera kodningen; XML-deklarationen anger den
Entitetsreferenser&amp; och ett bokstavligt & kan båda förekomma för samma teckenKanonisera, vilket löser upp entiteter konsekvent
CDATA vs escapad text<![CDATA[a<b]]> och a&lt;b är samma textinnehållJämför det tolkade värdet, inte de råa byten
Betydelsefulla blankstegInuti xml:space="preserve" spelar blanksteg roll och får inte tas bortTrimma inte blint; respektera xml:space
Självstängande taggar<x/> och <x></x> är identiskaKanonisera så att båda återges likadant

Textdiff vs strukturell diff

Allt ovan är en text-diff: snabb, visuell och perfekt för en person som läser en ändring. En strukturell diff går längre och beskriver ändringen i termer av XML-trädet: detta attribut ändrades, det barnelementet infogades vid den här sökvägen. Du vill ha en strukturell diff när ett program behöver tillämpa ändringen eller när elementordning verkligen inte spelar roll och du vill ignorera den. För daglig granskning räcker en textdiff av två formaterade dokument gott och väl.

Relaterade verktyg

XML är sällan det enda formatet du har att göra med. Jämför du API-payloads tillämpar JSON-jämförelse samma idé på JSON. Uppmärkta sidor är lättare att läsa på HTML-jämförelsesidan, och miljöinställningar passar bra på config-jämförelseverktyget.

Vanliga frågor

Laddar onlinejämförelse av XML-filer upp dem någonstans?
På comparetext.org körs diffen i din webbläsare. De två XML-filerna jämförs av JavaScript på din egen dator, så inget skickas till en server om du inte uttryckligen klickar på Spara eller Dela. Det gör det säkert för konfigurationsfiler, SOAP-meddelanden och annan data du inte vill klistra in på en webbplats som laddar upp vid varje tangenttryckning.
Varför visas varje rad som olik mellan mina två XML-filer?
Nästan alltid handlar det om formatering, inte verkliga ändringar. En fil är minifierad eller indragen med tabbar, den andra med två blanksteg, eller så är attributen i en annan ordning. Klicka på Formatera på båda sidor så att de använder samma indrag. Därefter krymper diffen oftast till de handfull värden som verkligen ändrats. För striktare normalisering, kanonisera båda filerna med xmllint --c14n först.
Spelar attributordning roll när man jämför XML?
Nej. I XML är attributen i ett element en oordnad mängd, så <a x="1" y="2"/> och <a y="2" x="1"/> är likvärdiga. En vanlig textdiff vet inte detta och flaggar omordningen som en ändring. Kanonisk XML sorterar attribut i en fast ordning, så att kanonisera båda sidor före jämförelse får falsklarmet att försvinna. Elementordning, däremot, är oftast betydelsefull.
Hur jämför jag XML och ignorerar namnrymdsprefix?
Namnrymdsprefix är lokala etiketter för en namnrymds-URI, så ns1:user och u:user bundna till samma URI är samma element. För att jämföra korrekt normaliserar du efter URI snarare än prefix. Enklaste sättet är att kanonisera båda dokumenten med xmllint --c14n, som skriver om namnrymdsbindningar konsekvent, och sedan diffa resultaten. En rå textdiff kan inte göra detta på egen hand.
Kan jag jämföra stora XML-filer utan att sidan fryser?
Ja, upp till en viss gräns. En radbaserad diff förblir snabb på filer med tusentals rader eftersom den jämför hela rader först i stället för varje tecken. Mycket stora filer (flera megabyte) hanteras bättre med ett kommandoradsverktyg som xmllint eller git diff, som strömmar data. För allt du bekvämt kan skrolla igenom i en webbläsare är en onlinediff det snabbare alternativet.
Vad är skillnaden mellan en textdiff och en strukturell diff av XML?
En textdiff jämför filerna rad för rad, på samma sätt som den skulle jämföra två uppsatser. En strukturell diff förstår XML-trädet, så den vet att ett omordnat attribut inte är en ändring och kan rapportera ett infogat element via dess sökväg. Textdiffar är snabbare och tillräckligt bra för de flesta granskningar när båda sidor är formaterade. Strukturella diffar spelar roll när ett program behöver tillämpa ändringen eller när du vill att elementordning ignoreras.

Redo att testa? Klistra in dina filer i XML-jämförelseverktyget och se vad som ändrats.