XML tools
Compare XML in Python: Define Tree Equality First
Compare parsed XML trees in Python while preserving element order, text, and namespace identities. Test attribute ordering and meaningful whitespace.
Decide which differences should count
To compare two XML files in Python, first define whether you need identical bytes, matching parsed trees, or matching application records. Those questions can produce different answers for the same pair of files. A deployment package may need a byte check, while an order importer may need to compare products by identifier.
This example compares parsed element trees. It ignores attribute order and namespace-prefix spelling, but preserves element order, attribute values, element text and text following child elements. It deliberately does not strip whitespace. Standard ElementTree parsing omits comments and processing instructions from this tree, so this is not an exact source-file comparison.
Build an explicit comparison value
Use this complete example with trusted, small XML strings. ElementTree represents namespaced element and attribute names using their namespace URI and local name. A sorted attribute tuple prevents serialization order from determining equality. Child tuples retain their original sequence.
import xml.etree.ElementTree as ET
def tree_value(element):
return (
element.tag,
tuple(sorted(element.attrib.items())),
element.text or "",
tuple(tree_value(child) for child in element),
element.tail or "",
)
left = ET.fromstring('<item code="A" count="2">Ready</item>')
right = ET.fromstring('<item count="2" code="A">Ready</item>')
print(tree_value(left) == tree_value(right)) # True
changed = ET.fromstring('<item count="3" code="A">Ready</item>')
print(tree_value(left) == tree_value(changed)) # False
For real files, obtain each root with ET.parse(path).getroot(). Let the XML parser handle the file's encoding declaration rather than first guessing an encoding and decoding the bytes yourself. Keep the original files unchanged; a comparison should not save a formatted version over either input.
Keep text and child order visible
Whitespace can be content. In mixed XML such as a paragraph containing an emphasized word, the spaces before and after that child determine whether the sentence reads correctly. Calling strip on every text node can erase a difference that a reader or downstream renderer needs.
The tail field is the text after a child element and before the next sibling or closing parent tag. Omitting it would incorrectly equate a paragraph ending with “ now” with one ending with “ later.” Test that case directly rather than relying only on an element containing one simple text value.
Similarly, a sequence containing a title followed by a note differs from one containing the note followed by the title. This function preserves that order. If your application treats a particular collection as unordered, introduce a separate rule for that collection using a documented unique identifier; do not sort every XML child globally.
Understand the namespace boundary
Two prefixes can refer to the same namespace URI. For example, a:item and b:item identify the same expanded element name when both prefixes map to urn:stock. A different namespace URI identifies a different name even if the displayed local name is item in both files.
Text values require more care. Some vocabularies put qualified names inside text or attribute values. The comparison above treats those values literally; it does not interpret a string such as a:Product as a namespace-aware reference. Add vocabulary-specific handling only when the data contract requires it.
Test the rule before using its result
Create cases for reordered attributes, a changed value, swapped children, a changed tail, namespace-prefix changes and a malformed closing tag. An XML parse failure is an error, not a successful “different” comparison. Catch ET.ParseError at the caller and report which input could not be parsed.
The ElementTree reference documents parsing and the tree model. For hostile or unrestricted XML, follow Python's XML security guidance and impose suitable resource limits; this small recursive example is not an upload-service security boundary.
The local XML comparison tool is useful for inspecting a small pair interactively. Its displayed comparison policy may differ from this Python function. Record the policy and input versions with any result used for an import or release decision.


