Oct 3, 2026 · 4 min read
The docs introduce the three comparison methods for repeated elements in a paragraph each. That’s enough to pick one — not enough to predict what the diff will actually say. This post runs the same XML through all three and shows the exact output, because the differences are bigger than the names suggest.
First, a one-line bridge from XML to the mechanics (detailed in how XML diff works): repeated sibling elements — three <event> tags under one <log> — become an array in the data tree. The comparison option you’re choosing applies to exactly those arrays:
event[0] with event[0], and recurses.Elements that appear only once per parent are matched by name, so this option only matters where the same tag repeats.
A log gains a single entry as the second event:
<log><event>1</event><event>2</event><event>3</event><event>4</event></log>
<log><event>1</event><event>5</event><event>2</event><event>3</event><event>4</event></log>
By Index watches the positional alignment shift and reports a cascade:
valueChanged log.event[1]
valueChanged log.event[2]
valueChanged log.event[3]
added log.event[4]
Four differences for one insertion. LCS isolates the common subsequence and says exactly what happened:
added log.event[1]
Unordered agrees:
added log.event[1]
Same steps, different order:
<steps><step>build</step><step>test</step><step>deploy</step></steps>
<steps><step>deploy</step><step>build</step><step>test</step></steps>
By Index flags every position:
valueChanged steps.step[0]
valueChanged steps.step[1]
valueChanged steps.step[2]
LCS keeps the longest ordered run it can and moves the rest:
deleted steps.step[2]
added steps.step[0]
Unordered stays silent:
(no differences)
Which answer is right? If this is an Ant build file or a WS-Security header where element order is contractual, By Index’s alarm is the correct one. If it’s a list of <tag> or <permission> entries, Unordered’s silence is. The wrong choice either buries you in noise or hides a real regression.
The trade-off the docs don’t spell out. A child element changes inside a repeated block:
<users>
<user id="1"><role>admin</role></user>
<user id="2"><role>user</role></user>
</users>
<users>
<user id="1"><role>admin</role></user>
<user id="2"><role>owner</role></user>
</users>
By Index recurses into the pair and pinpoints the exact spot — attributes, children, all of it:
valueChanged users.user[1].role
LCS matches elements by deep equality, and the second <user> no longer qualifies. The whole element drops out of the subsequence:
deleted users.user[1]
added users.user[1]
Unordered reports the same pair. You learn that user[1] differs, but not whether it was the id attribute, the <role>, or anything else. For structured repeated elements — <server> blocks in a config, <field> definitions in a schema — By Index is the only method that preserves field-level precision.
Unordered is multiset matching, not set matching — each match is consumed one-for-one:
<post><tag>a</tag><tag>b</tag><tag>b</tag><tag>c</tag></post>
<post><tag>c</tag><tag>a</tag><tag>b</tag></post>
Two <tag>b</tag> on the left, one on the right:
deleted post.tag[2]
If repetition counts are significant, that’s the behavior you want. If you truly want “same unique values”, deduplicate first — or expect Unordered to flag count differences.
| Method | Time complexity | Notes |
|---|---|---|
| By Index | O(n) comparisons | Plus one recursive comparison per pair |
| LCS | O(m × n) deep-equality checks | Each DP cell may recursively compare two entire subtrees |
| Unordered | O(m × n) worst case | Every unmatched base element scans the contrast side |
For a config with twenty <server> entries, any method is instant. For a data export with ten thousand <record> elements, LCS’s table — a full recursive comparison per cell — is genuinely expensive, and Unordered’s quadratic scan isn’t far behind. By Index is the only method that’s essentially free at any size.
| Your repeated elements are… | Best method | What you give up |
|---|---|---|
| Positional (pipeline steps, table rows, coordinates) | By Index | Insertions shift everything — noise on misaligned lists |
| Ordered, occasionally edited (logs, changelog entries) | LCS | Field-level precision on modified elements; O(m·n) cost |
| Orderless (tags, permissions, feature flags) | Unordered | Order changes are invisible; whole-element add/delete reporting |
The one-line version: By Index is precise but brittle, LCS is minimal but shallow, Unordered is calm but blind to order.
Try it on your own documents — the method switch lives in Settings, and re-running a comparison takes seconds: compare two XML documents now.