How to compare two XML files without the formatting noise
You export the same configuration twice, or run the same transformation on two servers, and the diff
says almost every line changed. Look closer and nothing did: one file is indented with tabs, the other with
two spaces; one serializer writes id before name, the other the other way round.
A text diff can't tell those apart from a real change, so the one value that actually differs is buried
under hundreds of lines of noise.
Why a line diff is the wrong tool for XML
A line diff compares characters. XML is a tree, and many different strings describe the same tree:
- Attribute order carries no meaning.
<item sku="A" qty="2"/>and<item qty="2" sku="A"/>are the same element, and serializers reorder attributes freely. - Whitespace between elements is formatting, not content. A pretty-printed file and the same document on one line are equivalent.
- Namespace prefixes are local nicknames.
<a:order>and<b:order>are the same element if both prefixes are bound to the same namespace URI. - Empty-element syntax:
<note/>and<note></note>parse to exactly the same thing.
To compare what two documents say, both have to be parsed and compared as trees.
The command-line way: canonicalize, then diff
If you have xmllint (part of libxml2), you can normalize both files with Canonical XML before diffing:
xmllint --c14n old.xml > old.c14n.xml
xmllint --c14n new.xml > new.c14n.xml
diff old.c14n.xml new.c14n.xml
Canonical XML sorts attributes and normalizes empty elements and quoting, which removes a good part of the
noise. It has two limits worth knowing: it keeps the whitespace between elements (so re-indented files still
differ: pass both through xmllint --format first), and it keeps the order of every element,
so a list that came out in a different order still shows up as changed.
Repeated elements: list or set?
That last point is the hard one. In most configuration and data files, a run of repeated elements, like
the <dependency> entries of a pom.xml or the <item> lines of an
order, is a set: the order they were written in doesn't matter, and a tool that regenerates the file
may well shuffle them. But a sequence of differently named elements usually does carry meaning:
<street> before <city> is part of the format.
Kotej's XML comparison follows exactly that rule: siblings with the same name are compared as a set, and differently named siblings keep their order. Attribute order, whitespace between elements and namespace prefixes are ignored, while element names, namespaces, attribute values and text are compared exactly. If the order of a repeated list does matter in your format, switch the file to the text comparison and you get a classic line diff.
Comparing XML in Kotej
- Open Kotej's XML diff and pick the two files, or drop them on each side.
Any extension that holds XML works:
.xml,.xsd,.xsl,.wsdl,pom.xml,.svg,.plist. - The verdict is one of three: identical (same bytes), equivalent (same tree, different formatting) or different.
- When they differ, open the file to see the changes side by side, as a unified diff, or word by word.
The files are parsed in your browser and never uploaded, which matters when the XML is a production configuration or a customer's data. The same comparison works on two whole folders, or two ZIP or JAR files, applied to every XML file inside them.
A common case: checking a migration
Equivalence is exactly the question when you change the thing that produces the XML: a new library version, a new serializer, a rewritten XSLT. You don't want the same bytes; you want the same document. Generate the output with the old and the new version, compare them as XML, and only real changes remain. There is a step-by-step guide for XSLT migrations.
Kotej