Home / Blog / XML tools

XML tools

Return XML Differences in Python with a Readable Report

Return a readable XML diff in Python, keep source changes distinct from semantic changes, and report malformed input separately from valid differences.

Choose a report that answers the review question

To return the differences between two XML files in Python, use a line diff when reviewers need to see the actual source edits. A parsed-tree comparison answers a different question. A source diff deliberately shows changed indentation, attribute ordering and comments because those are changes to the files being reviewed.

The function below validates both documents as XML, then returns a unified source diff. Validation prevents an incomplete export from silently entering a normal comparison report. It does not validate against an XSD or prove that the XML contains the expected business fields.

Return the diff instead of only printing it

This example accepts already decoded text so the caller controls file encoding. It normalizes line-ending representation through splitlines, retains other line content, and uses visible original and revised labels. The result is a string that can be saved, displayed as text or checked in a test.

import difflib
import xml.etree.ElementTree as ET

def xml_source_diff(left_text, right_text):
    ET.fromstring(left_text)
    ET.fromstring(right_text)
    left_lines = left_text.splitlines()
    right_lines = right_text.splitlines()
    report = difflib.unified_diff(
        left_lines, right_lines,
        fromfile="original.xml", tofile="revised.xml",
        n=2, lineterm="",
    )
    return "\n".join(report)

before = '<order>\n  <count>2</count>\n</order>'
after = '<order>\n  <count>3</count>\n</order>'
result = xml_source_diff(before, after)
print(result)

The output includes a removed line containing count 2 and an added line containing count 3. Header lines name the two versions, while the hunk header locates the changed region. An empty result means no differences under this line-based policy; it does not certify identical original bytes.

Explain the normalization that the code performs

Because splitlines removes line terminators, a Windows CRLF file and a Unix LF file with the same line contents compare equal here. A final newline difference is also not represented by this function. If those details matter, compare bytes separately or use a source-diff implementation that explicitly reports them.

A compact XML document may occupy one long line. A line diff can then show the entire line as replaced after one small attribute change. That result is accurate but inconvenient to review. Prefer a producer that emits stable formatting, or offer a separate parsed comparison; do not secretly pretty-print the input and call the result an exact source diff.

Pretty-printing can insert whitespace into mixed content. It can also move the reported line numbers away from the original file. If you provide a formatted view, label it as a transformed representation and retain access to the unmodified source.

Integrate the function without hiding failures

Read a known UTF-8 export using an explicit UTF-8 encoding, then pass the two strings to the function. If the producer uses another declared encoding, decode it correctly before this stage or choose a byte-oriented parsing workflow. Do not replace undecodable characters just to make the comparison continue.

Catch ET.ParseError around the call and report an invalid input state. Keep the previous diff out of the current result area when a new comparison fails. Otherwise an operator could mistake yesterday's successful report for the result of today's malformed export.

When rendering the returned string in a web interface, assign it as text rather than HTML. XML source can contain angle brackets and text that resembles executable markup. A comparison report should display that source literally, including any suspicious content, without activating it.

Test what readers will actually see

Verify an equal pair, one changed value, an inserted child, a removed comment, differently ordered attributes and a malformed document. For the changed value fixture, assert that both the old and new count lines appear; checking only that the result is nonempty would miss an unrelated error message.

The Python unified_diff reference describes its reporting options. Compare its source-oriented output with the local XML comparison tool when you also need to inspect parsed structure. Keep the two report types clearly labelled so a formatting change does not become a false business-data alarm.