XML Formatter & Validator
Pretty-print, minify or just check XML — live. Mixed content and xml:space="preserve" are copied byte for byte, never re-indented.
1 Your XML
Whatever you type reformats as you type it — there is no Generate button. The example loaded above is deliberately ragged, and it contains one mixed-content <summary> and one xml:space="preserve" block so you can watch both survive untouched.
2 Result
Formatted XML
No network calls for the formatting — no DTD fetch, no external entity resolution, no upload. The tokenizer runs in this tab and contains no fetch, no XHR and no DOMParser, so your document cannot be uploaded and a SYSTEM or PUBLIC identifier in a DOCTYPE is treated as opaque text rather than something to go and get. That closes XXE by construction, not by a filter. The page does load one cookieless pageview counter from stats.dankdev.com, which records this page’s address and title and nothing you paste.
Exactly what this changes — and what it will not touch
A formatter earns its keep by changing only the whitespace a parser reports as insignificant. Here is the complete list, both ways round, so you never have to guess whether the file you paste back is the file you started with.
Changed
- Whitespace between sibling elementsReplaced with your chosen indent, or deleted in Minify. This is the only whitespace an XML parser is free to discard, and it is the entire job.
- Whitespace inside a start tag
<a\n b="1"\n c="2"/>becomes<a b="1" c="2"/>. Nothing between attributes is ever reported to an application. - Blank lines between elementsGone, along with any trailing spaces on those lines.
- Line endings between elementsPretty mode writes LF. Line endings inside text content are left exactly as they were, so a CRLF file comes back mixed — convert deliberately if that matters to you.
- A space before a self-closing slash
<a />becomes<a/>; that space is tag whitespace too.
Never touched
- Mixed contentAn element with both element children and real text children keeps its inner bytes exactly — and is listed above the output by name with its line number, so you can see which regions were left alone rather than wondering.
xml:space="preserve"The whole subtree is frozen, in Pretty and in Minify alike. Deliberately conservative: once a subtree is frozen it stays frozen even if a descendant setsxml:space="default".- Text-only element content
<pw> hunter2 </pw>keeps its padding, and<a> </a>is not an empty element — it is never collapsed to<a/>. - CDATA, comments, PIs, the declaration, the DOCTYPECopied verbatim, internal subset and all. Minify keeps comments: deleting documentation is not compression.
- AttributesOrder preserved, never alphabetised. Quote style preserved. Values preserved character for character.
<a></a>versus<a/>Identical to a parser, not identical as bytes. Whichever you wrote is what you get back.- Entity and character referencesNever expanded, never respelled:
&stays&,éstayséand is not turned intoé. - A leading byte-order markKept if it was there, not added if it was not.
The bug every XML pretty-printer has
Take a line out of an Atom feed:
<summary>Fixes the <code><meta></code> bug and <em>two</em> crashes.</summary>
A formatter that walks the tree and puts every child on its own line produces this:
<summary>
<code><meta></code>
<em>two</em>
</summary>
The words are gone. Even a formatter careful enough to keep them has inserted a newline and two spaces before <code> and after </em>, and those are character data: an XML parser hands them to the application, and the rendered sentence gains line breaks it never had. Nothing errors. The file still parses. It just says something different.
That case has a name — mixed content, an element with both element children and non-whitespace text children — and it is why this page detects it and copies the element’s inner source through byte for byte instead of re-indenting it. The elements it skipped are listed above the output with their line numbers, so “why is this one line still long?” has a visible answer.
Why a real tokenizer and not a regular expression
Every shortcut breaks on something people actually paste. A < inside a <![CDATA[…]]> section is a literal character, not a tag — and CDATA is how half the world puts HTML inside an RSS <description>. A > inside a DOCTYPE’s internal subset, as in <!ENTITY sign "a > b">, does not end the DOCTYPE. An attribute quoted with ' may contain as many " as it likes. So this walks the document character by character with a real element stack, which is also what makes precise errors possible: </novel> closing a <book> is reported at line 4, column 3, naming both tags and the line the <book> opened on.
What it checks, and what it deliberately does not
Validate-only mode checks well-formedness: tags balanced and correctly nested, exactly one root element, legal element and attribute names, every attribute value quoted, no attribute repeated, valid entity and character-reference syntax, no bare &, no stray ]]>, no control characters that XML 1.0 forbids outright. That is the layer nearly every real XML failure lives at — the bare ampersand in a query string inside a sitemap is the single commonest one.
It does not check validity against a DTD, an XSD or a RELAX NG schema, and it never will, because validating means fetching and running a schema and this page fetches nothing. Namespace problems are reported as warnings rather than errors: an unbound prefix such as <soap:Envelope> with no xmlns:soap in scope is genuinely broken to a namespace-aware parser, but people paste fragments out of larger documents all day, and refusing the paste would be less useful than saying so.
Honest limits
Everything happens in this tab’s memory, so a 50 MB export will make the browser work hard and a phone may give up; a few megabytes is comfortable. Only the first error is reported, because a tokenizer that has lost its place invents the rest. Undeclared entities are passed through unresolved with a warning, so a document that leans on DTD-declared entities formats fine but does not expand. And the output uses LF line endings between elements regardless of what came in — the text inside elements keeps whatever endings it had, which means a CRLF file comes back mixed.
Frequently asked
Can pretty-printing XML change what the document means? Yes, and that is the whole difficulty. Whitespace between sibling elements carries no information, so re-indenting it is safe. Whitespace beside text does carry information: turning <p>Hello <b>world</b>.</p> into an indented block inserts newlines and spaces into the character data, and the file still parses — it just says something else now. This tool detects that case and copies the element’s inner bytes through unchanged instead, then names the element and its line number so you can see exactly which regions it refused to touch.
Exactly what does it do to whitespace? It changes two things and nothing else. Whitespace between sibling elements is replaced with your chosen indent, and whitespace inside a start tag, between attributes, collapses to one space. Everything else is left alone: text inside an element that holds only text, including <a> </a> and any leading or trailing spaces, mixed content, anything under xml:space="preserve", CDATA sections, comments, processing instructions, the XML declaration and the DOCTYPE.
Is my XML uploaded anywhere, and can this page be used for an XXE attack? No to both. The tokenizer is about a thousand lines of JavaScript in this page and it has no fetch, no XHR and no DOMParser in it, so there is no code path that could send your document anywhere or pull anything in. External entities are the specific danger with XML, and they are structurally impossible here: nothing is ever expanded, so a SYSTEM or PUBLIC identifier in a DOCTYPE is treated as opaque text and never dereferenced.
Does it expand entity references, or leave them exactly as written? Never. The five predefined references — & < > " ' — and numeric references such as é or é are checked for valid syntax and then copied through with their exact original spelling. Anything else, for instance, would need a DTD declaration to resolve, and reading a DTD is precisely the hole this page refuses to have, so it is passed through untouched with a warning telling you it was not resolved.
Does this validate my XML against a schema? No. It checks well-formedness — tags balanced and correctly nested, one root element, legal names, quoted attribute values, no duplicate attributes, valid entity syntax, no stray & or ]]> — which is the layer almost every real XML failure lives at. It does not check validity against a DTD, an XSD or a RELAX NG schema, because doing that means fetching and executing a schema, and this page fetches nothing. Namespace problems such as an unbound prefix are reported as warnings rather than errors, because people paste fragments.
Why does the error say “line 4, column 3” instead of just “invalid XML”? Because a position is the only part of an error message you can act on. The tokenizer tracks line and column as it walks, so the first problem is reported where it is, with the offending line echoed and a caret under the character, plus a sentence in plain English: an end tag that does not match the element still open, a bare ampersand in a query string, an HTML-style attribute with no value. Later problems are not guessed at, because a parser that has lost its place invents them.
What does Minify actually remove? Only the whitespace between sibling elements — the indentation and the blank lines. Comments are kept, because a minifier that deletes documentation without asking is deleting your work. Text content, CDATA, mixed content, preserved subtrees, the declaration and the DOCTYPE all come through unchanged, so minifying and then pretty-printing returns you to the same tree you started with.