Sammenlign to XML-filer og se, hvad der er ændret
Den hurtigste måde at sammenligne to XML-filer på er at indsætte begge i et side-om-side diff-værktøj, formatere dem ens og læse de linjer, det fremhæver. Selve sammenligningen er den nemme del. Støjen er det, der driller folk: omarrangerede attributter, mellemrum mellem tags og namespace-præfikser kan få to filer, der betyder det samme, til at se ud, som om de slet ikke har noget til fælles.
Denne guide gennemgår, hvordan man får en ren og troværdig XML-diff. Vi ser på, hvorfor to ækvivalente dokumenter driver fra hinanden på papiret, hvilke metoder der er værd at kende, og et gennemarbejdet eksempel, du kan følge. Vil du bare have værktøjet, klarer vores XML-sammenligningsside det hele i browseren.
Hvorfor XML-filer er bedragerisk svære at sammenligne
XML har en streng grammatik (se W3C's XML-specifikation), men giver skribenter stor frihed i, hvordan teksten er sat op. To dokumenter kan beskrive præcis de samme data og stadig være forskellige byte for byte. En almindelig tekstdiff forstår intet af dette og flagger derfor det hele.
Her er det vigtigste at holde fast i: i XML er rækkefølgen af attributter
på et element ikke betydningsfuld. Det
XML Information Set
behandler attributter som en uordnet mængde. Så
<user id="7" role="admin"/> og
<user role="admin" id="7"/> bærer den samme information,
selv om en linjediff maler dem røde og grønne. Elementrækkefølge derimod
betyder som regel noget.
| Hvad du ser i diffen | Er det en reel ændring? | Hvad du skal gøre |
|---|---|---|
| Attributter i en anden rækkefølge | Nej, attributrækkefølge er ikke betydningsfuld | Kanoniser begge sider |
| 2-mellemrums vs 4-mellemrums indrykning | Nej | Formater begge ens |
| Mellemrum mellem elementer | Som regel ikke | Formater, eller fjern ubetydelige mellemrum |
<br/> vs <br></br> | Nej, samme tomme element | Kanoniser begge sider |
| Et andet namespace-præfiks for samme URI | Nej, præfikser er vilkårlige etiketter | Sammenlign efter namespace-URI, ikke præfiks |
| Underelementer i en anden rækkefølge | Som regel ja, elementrækkefølge betyder noget | Undersøg, dette er sandsynligvis reelt |
Den sidste række er den, man skal holde øje med. Attributrækkefølge er fri, men rækkefølgen af underelementer er en del af dokumentet i de fleste skemaer. Vil du have detaljerne om, hvordan en parser ser alt dette, har MDN en solid reference om at parse XML med DOMParser.
Fire måder at sammenligne XML, og hvornår man griber til hver
Der er ingen enkelt bedste metode. Det afhænger af, hvor filerne ligger, og hvad du forsøger at finde ud af. Sådan klarer de gængse muligheder sig.
| Metode | Bedst til | Indsats | Forstår XML? |
|---|---|---|---|
| At læse det igennem selv | Bittesmå filer, et eller to elementer | Lav | Nej, du er parseren |
| Online diff-værktøj | Hurtige tjek, indsætning fra hvor som helst | Lav | Med formatering, ja |
Kommandolinje (xmllint) | Filer på disk, scripting, kanonisk form | Mellem | Ja, med --c14n |
IDE eller git diff | Filer der allerede er i et repo | Lav ved commit | Linjebaseret som standard |
For de fleste vinder et browserværktøj på hastighed: intet at installere,
og du kan indsætte et uddrag direkte fra en konfigurationsfil eller et
SOAP-svar. Hagen er formateringsstøj, som vi tager fat på nu. Lever du i
terminalen, er xmllint fra
libxml2
værktøjet at kende.
Den hurtigste rene sammenligning, trin for trin
Det er rutinen, jeg bruger, når nogen rækker mig to konfigurationsfiler og spørger "hvad er forskellen?" Det tager omkring femten sekunder.
- Åbn XML-sammenligningsværktøjet.
- Indsæt originalen til venstre, den nye version til højre.
- Klik på Formater på begge sider, så de bruger samme indrykning.
- Søg efter reelle forskelle. Grøn er tilføjet, rød er fjernet, og en ændret værdi vises som en af hver.
- Ignorer rækkerne, der kun er attributomarrangering eller mellemrum.
Trin tre er det meste af tricket. Når begge dokumenter bruger samme indrykning, er det eneste, der er tilbage at fremhæve, hvad der faktisk er ændret. Vores diff-motor bygger på Googles diff-match-patch, som sammenligner linje for linje først, så den forbliver hurtig selv på lange filer.
Et gennemarbejdet eksempel
Sig, at du gennemgår en ændring i en tjenestekonfiguration. Her er før:
<user id="7" role="editor">
<name>Ada Lovelace</name>
<active>true</active>
<seats>3</seats>
</user>
Og her er efter, som en kollega rakte den til dig:
<user role="admin" id="7">
<name>Ada Lovelace</name>
<active>true</active>
<seats>5</seats>
<team>platform</team>
</user>
Smid dem ind i en rå linjediff, og den allerførste linje ser ændret ud,
fordi id og role har byttet plads. Formater begge,
sammenlign efter betydning, og den reelle historie er kort:
| Node | Før | Efter | Ændring |
|---|---|---|---|
@role | editor | admin | Ændret |
seats | 3 | 5 | Ændret |
team | — | platform | Tilføjet |
@id | 7 | 7 | Ingen ændring (bare flyttet) |
name | Ada Lovelace | Ada Lovelace | Ingen ændring |
Tre reelle ændringer: en rolleforfremmelse, et antal pladser og et nyt
team-element. Attributbyttet var støj. Den forfremmelse fra
editor til admin er præcis den slags, du vil
fange i gennemgangen, og den er let at overse, når den er begravet under en
linje, diffen fejlagtigt flaggede.
Kanonisk XML: den rigtige måde at ignorere støj på
At formatere begge sider håndterer indrykning, men der findes en standard bygget netop til dette problem. Kanonisk XML, defineret af W3C i Canonical XML 1.1, omskriver et dokument til én normaliseret form: attributter sorteret, tomme elementer udvidet, mellemrum i tags normaliseret og standardattributter gjort eksplicitte. To ækvivalente dokumenter producerer identisk kanonisk output. Det er XML-ækvivalenten til at sortere JSON-nøgler.
xmllint --c14n old.xml > old.c14n.xml
xmllint --c14n new.xml > new.c14n.xml
diff old.c14n.xml new.c14n.xml
Nu rapporterer diff kun indhold, der virkelig er ændret, fordi
begge filer er normaliseret på samme måde. Vil du bare have læsbar
indrykning i stedet for streng kanonisk form, formaterer
xmllint --format file.xml den, hvilket er
terminal-ækvivalenten til at klikke på Formater i browseren.
Namespaces: den del, der forvirrer alle
XML-namespaces lader to dokumenter bruge det samme vokabular med forskellige
præfiks-etiketter. <ns1:user> bundet til en URI og
<u:user> bundet til samme URI er det samme
element; præfikset er bare et lokalt kælenavn. En tekstdiff ser
ns1 mod u og flagger en ændring, der ikke er en.
Løsningen er at sammenligne efter namespace-URI frem for præfikset, hvilket
er præcis, hvad kanonisering gør. Specifikationen
Namespaces in XML
er referencen, hvis du skal afgøre en diskussion om det.
Almindelige faldgruber at holde øje med
| Faldgrube | Hvorfor den bider | Løsning |
|---|---|---|
| Tegnkodning | En UTF-8- og en UTF-16-fil kan indeholde samme tekst, men være forskellige byte for byte | Normaliser kodningen; XML-deklarationen angiver den |
| Entitetsreferencer | & og et bogstaveligt & kan begge optræde for samme tegn | Kanoniser, hvilket opløser entiteter konsekvent |
| CDATA vs escaped tekst | <![CDATA[a<b]]> og a<b er det samme tekstindhold | Sammenlign den parsede værdi, ikke de rå bytes |
| Betydningsfulde mellemrum | Inden i xml:space="preserve" betyder mellemrum noget og må ikke fjernes | Trim ikke blindt; respekter xml:space |
| Selvlukkende tags | <x/> og <x></x> er identiske | Kanoniser, så begge gengives ens |
Tekstdiff vs strukturel diff
Alt ovenstående er en tekst-diff: hurtig, visuel og perfekt for en person, der læser en ændring. En strukturel diff går videre og beskriver ændringen i form af XML-træet: dette attribut blev ændret, det underelement blev indsat ved denne sti. Du vil have en strukturel diff, når et program skal anvende ændringen, eller når elementrækkefølge virkelig ikke betyder noget, og du vil have den ignoreret. Til daglig gennemgang er en tekstdiff af to formaterede dokumenter rigeligt.
Relaterede værktøjer
XML er sjældent det eneste format, du har med at gøre. Sammenligner du API-payloads, anvender JSON-sammenligning den samme idé på JSON. Opmærkede sider er lettere at læse på HTML-sammenligningssiden, og miljøindstillinger passer godt på config-sammenligningsværktøjet.
Ofte stillede spørgsmål
- Uploader online-sammenligning af XML-filer dem nogen steder?
- På comparetext.org kører diffen i din browser. De to XML-filer sammenlignes af JavaScript på din egen maskine, så intet sendes til en server, medmindre du udtrykkeligt klikker på Gem eller Del. Det gør det sikkert for konfigurationsfiler, SOAP-beskeder og andre data, du ikke vil indsætte på et site, der uploader ved hvert tastetryk.
- Hvorfor vises hver linje som forskellig mellem mine to XML-filer?
- Næsten altid er det formatering, ikke reelle ændringer. Den ene fil er minificeret eller indrykket med tabs, den anden med to mellemrum, eller attributterne er i en anden rækkefølge. Klik på Formater på begge sider, så de bruger samme indrykning. Derefter skrumper diffen som regel til de få værdier, der virkelig er ændret. For strengere normalisering, kanoniser begge filer med xmllint --c14n først.
- Betyder attributrækkefølge noget, når man sammenligner XML?
- Nej. I XML er attributterne på et element en uordnet mængde, så
<a x="1" y="2"/>og<a y="2" x="1"/>er ækvivalente. En almindelig tekstdiff ved ikke dette og flagger omarrangeringen som en ændring. Kanonisk XML sorterer attributter i en fast rækkefølge, så når man kanoniserer begge sider før sammenligning, forsvinder den falske alarm. Elementrækkefølge derimod er som regel betydningsfuld. - Hvordan sammenligner jeg XML og ignorerer namespace-præfikser?
- Namespace-præfikser er lokale etiketter for en namespace-URI, så
ns1:userogu:userbundet til samme URI er det samme element. For at sammenligne korrekt normaliserer du efter URI frem for præfiks. Den nemmeste måde er at kanonisere begge dokumenter med xmllint --c14n, som omskriver namespace-bindinger konsekvent, og derefter diffe resultaterne. En rå tekstdiff kan ikke gøre dette på egen hånd. - Kan jeg sammenligne store XML-filer uden at siden fryser?
- Ja, op til et vist punkt. En linjebaseret diff forbliver hurtig på filer med tusindvis af linjer, fordi den sammenligner hele linjer først i stedet for hvert tegn. Meget store filer (flere megabyte) håndteres bedre med et kommandolinjeværktøj som xmllint eller git diff, der streamer dataene. For alt, du komfortabelt kan scrolle igennem i en browser, er en online-diff den hurtigere mulighed.
- Hvad er forskellen mellem en tekstdiff og en strukturel diff af XML?
- En tekstdiff sammenligner filerne linje for linje, på samme måde som den ville sammenligne to essays. En strukturel diff forstår XML-træet, så den ved, at et omarrangeret attribut ikke er en ændring, og kan rapportere et indsat element via dets sti. Tekstdiffs er hurtigere og gode nok til de fleste gennemgange, når begge sider er formateret. Strukturelle diffs betyder noget, når et program skal anvende ændringen, eller når du vil have elementrækkefølge ignoreret.
Klar til at prøve det? Indsæt dine filer i XML-sammenligningsværktøjet og se, hvad der er ændret.