# XML Diff

Compare two XML documents structurally and see exactly what was added, removed or changed, with full element paths — in your browser. Nothing is uploaded.

## Run it

- **CLI:** `gizza tool xml-diff '<catalog><book id="1"><title>Rust</title></book></catalog>' 'right=<catalog><book id="2"><title>Rust 2</title></book></catalog>'`
- **Web:** https://gizza.ai/tools/xml-diff/
- **Agents:** machine-readable descriptor (parameters JSON Schema) at https://gizza.ai/tools/xml-diff/tool.json

## Inputs

- `left` — First XML (left / old) _(field)_
- `right` — Second XML (right / new) _(field)_
- `strategy` — Sibling matching _(field)_
- `ignore_whitespace` — Ignore insignificant whitespace _(field)_
- `ignore_comments` — Ignore comments _(field)_
- `ignore_namespaces` — Ignore namespace prefixes _(field)_
- `numeric_text` — Compare numbers numerically (1 = 1.0) _(field)_
- `format` — Report format _(field)_
- `indent` — JSON indent (spaces; 0 = minify) _(field)_

## Output

- Diff report (text)

## Query parameters

Open the tool pre-filled and auto-run via URL:

- `left` — First XML (left / old)
- `right` — Second XML (right / new)
- `strategy` — Sibling matching
- `ignore_whitespace` — Ignore insignificant whitespace
- `ignore_comments` — Ignore comments
- `ignore_namespaces` — Ignore namespace prefixes
- `numeric_text` — Compare numbers numerically (1 = 1.0)
- `format` — Report format
- `indent` — JSON indent (spaces; 0 = minify)

Example: `https://gizza.ai/tools/xml-diff/?left=%3Ccatalog%3E%3Cbook%20id%3D%221%22%3E%3Ctitle%3ERust%3C%2Ftitle%3E%3C%2Fbook%3E%3C%2Fcatalog%3E&right=%3Ccatalog%3E%3Cbook%20id%3D%222%22%3E%3Ctitle%3ERust%202%3C%2Ftitle%3E%3C%2Fbook%3E%3C%2Fcatalog%3E&strategy=lcs&ignore_whitespace=true&ignore_comments=true&ignore_namespaces=true&numeric_text=true&format=json&indent=2`

---

## About this tool

**XML Diff** compares two XML documents by **structure**, not by text lines. A
line-based diff flags every re-indentation and every reordered attribute; this
one parses both documents into element trees and reports only the differences
that actually change the data.

Paste your **first (left / old)** document and your **second (right / new)**
document. The tool walks both trees in parallel:

- **Elements** are matched child by child, using the sibling-matching strategy
  you choose.
- **Attributes** are compared as a sorted map, so attribute **order never
  matters**.
- **Text** is whitespace-normalized by default, so indentation and line breaks
  never matter. **CDATA** is folded into the element's text, so
  `<a><![CDATA[hi]]></a>` equals `<a>hi</a>`.
- Every difference is reported with an **XPath-style path** —
  `/catalog/book[2]` for an element, `/catalog/book[2]/@id` for an attribute,
  `/catalog/book[2]/title/text()` for text — and classified as **added**,
  **removed** or **changed**.

### Worked example

Comparing

```xml
<catalog><book id="1"><title>Rust</title></book></catalog>
```

with

```xml
<catalog><book id="2"><title>Rust 2</title></book></catalog>
```

gives this report (JSON format, indent 2):

```json
{
  "equal": false,
  "added": 0,
  "removed": 0,
  "changed": 2,
  "changes": [
    { "path": "/catalog/book/@id", "kind": "changed", "old": "1", "new": "2" },
    { "path": "/catalog/book/title/text()", "kind": "changed", "old": "Rust", "new": "Rust 2" }
  ]
}
```

The same comparison with **Report format = text** gives:

```text
2 differences: 0 added, 0 removed, 2 changed
~ /catalog/book/@id  1 -> 2
~ /catalog/book/title/text()  Rust -> Rust 2
```

### Options

- **Sibling matching** — *Smart alignment* (default) aligns identical subtrees
  first, so inserting one element in the middle is reported as a single
  addition instead of shifting every later sibling. *By position* compares
  siblings strictly index by index. *Ignore order* treats siblings as a set, so
  a reordered document compares as equal.
- **Ignore insignificant whitespace** (on by default) collapses whitespace runs
  and drops whitespace-only text nodes. Turn it off for an exact text
  comparison.
- **Ignore comments** (on by default). Turn it off and comment changes appear
  as `comment()` nodes.
- **Ignore namespace prefixes** makes `ns:book` and `p:book` match and drops
  `xmlns` declarations from the comparison.
- **Compare numbers numerically** makes `1` equal `1.0` and `2.50` equal `2.5`
  in both text and attribute values.
- **JSON indent** sets the output indentation (0 minifies); it is ignored for
  the text report.

Everything runs **locally in your browser** via WebAssembly — your documents are
never uploaded.

### Limits and edge cases

- Each document is limited to **1 MB** and **500 nesting levels**; larger input
  is rejected with a clear error naming the side that failed.
- **Mixed content**: an element's direct text nodes are folded into one value,
  so moving text around between sibling elements inside the same parent is not
  reported positionally.
- The **XML declaration, DOCTYPE and processing instructions** are not
  compared.
- Custom **DTD-defined entities** are compared as written when they cannot be
  resolved.

### Handy for

- Reviewing changes between two config, POM, SOAP or feed versions.
- Checking that a re-serialized document is semantically unchanged.
- Producing a machine-readable change report for tests, CI or audits.

### FAQ

<details>
<summary>Does attribute order or indentation matter?</summary>

No. Attributes are compared as a sorted map, so `<a x="1" y="2"/>` and `<a y="2" x="1"/>` are equal. Indentation and line breaks are ignored too, as long as **Ignore insignificant whitespace** is on (it is by default) — turn it off if you need a byte-faithful text comparison.

</details>

<details>
<summary>How is a reordered document handled?</summary>

By default (*Smart alignment*) sibling order is significant, so swapping two children is reported as differences. Pick **Ignore order — treat siblings as a set** when the order carries no meaning: identical subtrees pair up in any order, then the leftovers pair up by element name.

</details>

<details>
<summary>What happens when an element is inserted in the middle of a list?</summary>

With *Smart alignment* the untouched siblings on both sides of the insertion are matched as anchors, so you get exactly one `added` entry. With *By position* the same edit shifts every later sibling, so you get a string of `changed` entries plus one `added` at the end — useful when position itself is what you're checking.

</details>

<details>
<summary>Are namespaces compared?</summary>

Yes, by default: `ns:book` and `p:book` are different element names even when both prefixes are bound to the same URI, and `xmlns` declarations are ordinary attributes. Switch on **Ignore namespace prefixes** to compare local names only and skip `xmlns` declarations entirely.

</details>

<details>
<summary>Why do I get "the first (left) XML is not well-formed"?</summary>

Both inputs must be well-formed XML, and the error names the side that failed plus the byte position. Common causes: an unclosed tag, a stray `&` that isn't an entity, or mismatched start/end tag names. The tool never repairs input — it only reports.

</details>

<details>
<summary>Is this the same as a text diff of the two files?</summary>

No. A text diff works line by line, so reformatting a document produces a huge diff even when nothing changed. This tool compares the parsed trees, so it reports only real structural and value differences — and it locates each one by element path rather than by line number.

</details>

## Related tools

- [Absolute value, sign, or negation for a whole column](https://gizza.ai/tools/absolute-value-transformer/): Paste a column of numbers and apply absolute value, sign extraction (-1/0/1), sign flipping, or force-negative to every value at once, with rounding and an audit table.
- [Adjacency Matrix Converter](https://gizza.ai/tools/adjacency-matrix-converter/): Convert a graph between edge list, adjacency matrix, and incidence matrix — directed or undirected, weighted or not. Free, private, runs in your browser.
- [Amazon Order Analyzer](https://gizza.ai/tools/amazon-order-analyzer/): Paste an Amazon order-history CSV export to summarize total spend by month, top items, and category breakdowns. Browser-only, private, with Markdown or JSON output.
- [ARFF Converter](https://gizza.ai/tools/arff-converter/): Convert Weka ARFF datasets to CSV and CSV tables back to ARFF locally — nominal attributes, numeric types, dates, sparse rows, missing values, and type rows.
- [Avro to JSON Converter](https://gizza.ai/tools/avro-to-json/): Decode Apache Avro Object Container Files (.avro / OCF) to JSON, NDJSON, or the embedded schema — no .avsc needed, free and private in your browser.
