An XML parser is unforgiving in a way HTML parsers are not. A browser will happily render a page with an unclosed <div>. An XML parser reading the same mistake stops dead and returns nothing usable. That strictness is the point of the format, and it is also why a single stray character in a 4,000 line SOAP response takes an afternoon to find.
This page runs your document through the same DOMParser your browser uses for XHTML and feeds back what it says. When the parse fails you get the message, the line, and the column. When it succeeds you get a structural summary you can sanity check against what you expected the file to contain.
Well-formed and valid are two different checks
These words get used interchangeably in bug reports and they mean different things in the XML spec.
Well-formed
Grammar only. One root element, every open tag closed, tags nested rather than overlapped, attribute values quoted, reserved characters escaped, names following the naming rules.
This page checks this
Valid
Conformance to a declared grammar: a DTD, an XSD schema, or a RELAX NG file. Are the right elements present, in the right order, with the right data types, inside the right parents.
This page does not check this
A document is often well-formed and wrong. <order><quantity>banana</quantity></order> parses without complaint. If the schema says quantity is xs:integer, the receiving system rejects it anyway. When an API returns a schema error rather than a parse error, this tool has already told you everything it knows.
Five failures behind most parser errors
Each block below is a real broken document. Load one into the editor to see how the parser reports it, then fix it in place.
1. A bare ampersand in text
Most common by a wide marginAn ampersand starts an entity reference. Written on its own, the parser reads forward looking for a name and a semicolon, finds a space instead, and gives up. Company names and query strings copied out of a database trigger this constantly.
<vendor><name>Marks & Spencer</name></vendor>Fix: write & for a literal ampersand, or wrap the text in <![CDATA[ ... ]]> when the string is full of markup characters. The other four that need escaping in text or attributes are <, >, ", and '.
2. Tag names differing by case
Hand-edited filesXML is case sensitive from top to bottom. <Book> and <book> are unrelated elements, so a closing tag with different capitalisation reads as a mismatch. People coming from HTML hit this on their first day.
<catalog><Book>Clean Code</book></catalog>Fix: match the case exactly. Pick one convention for the whole file and stay with it.
3. Two root elements
Concatenated files and log dumpsAn XML document has exactly one outermost element. Appending records to a file or joining two responses produces a second root, and the parser reports trouble at the start of the second one even though the file looked fine up to there.
<record id="1"><value>12</value></record><record id="2"><value>34</value></record>Fix: wrap both in a container element such as <records>. If the source is an append-only stream, read it record by record rather than parsing the whole file as one document.
4. An element name starting with a digit
Generated from spreadsheetsNames start with a letter or an underscore. Anything generated from spreadsheet column headers or database fields called 2024_total produces names the parser refuses. A name also cannot contain a space, and cannot start with the letters xml in any casing.
<report><2024_total>98000</2024_total></report>Fix: prefix it, as in <y2024_total>, or move the number into an attribute: <total year="2024">. The attribute version is usually the better data model anyway.
5. An undeclared namespace prefix
SOAP and RSS payloadsA prefix such as soap: means nothing on its own. It has to be bound with an xmlns:soap declaration on that element or an ancestor. Copying an inner fragment out of a larger SOAP envelope leaves the prefix behind and the binding upstream.
<soap:Body><getPriceResponse><price>34.50</price></getPriceResponse></soap:Body>Fix: carry the declaration down with the fragment: <soap:Body xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">. The URI is an identifier, not an address, so nothing is fetched from it.
The reported line is where the parser gave up, not where you slipped
This trips up people more than the errors themselves. A parser reads forward and reports trouble at the first character it cannot reconcile with what it has already seen. With an unclosed <item> on line 40, the mismatch surfaces on line 300 at the closing </order>, because that is the first tag the parser can prove is wrong.
So read the reported line as a ceiling. Your mistake is on that line or above it. Two habits narrow the gap fast: indent the document first, because a mis-nested tag becomes visible as a step in the indentation, and cut the file in half and check each part to bisect toward the break.
The wording of the message depends on the browser engine, not on this page. Chrome and Edge report Opening and ending tag mismatch. Firefox says mismatched tag. Safari phrases it differently again. Same document, three sentences. Compare the line numbers rather than the prose when you look up an error.
What the numbers in the report mean
- Elements counts every element node in the tree, root included. Compare it against the record count you expected. A file that should hold 500 orders and reports 12 elements usually parsed only a truncated download.
- Attributes is the total across all elements, not distinct names. Namespace declarations are attributes too, so a heavily namespaced envelope inflates this number.
- Max depth is the deepest nesting level below the root. Deep trees are slow to query with XPath and awkward to map to flat structures. Past eight or nine levels, the data model is usually worth a second look.
- Text nodes counts only nodes with non-whitespace content, so indentation is not counted. If this reads zero on a document you expected to hold values, the payload is probably all attributes.
- Namespaces lists the distinct URIs bound anywhere in the document. Two prefixes pointing at one URI count once, because the prefix is a local alias and the URI is the real identity.
Where this checker stops
Being honest about the ceiling saves you from trusting a green badge too far.
- No DTD or XSD validation. A
<!DOCTYPE>declaration is parsed as syntax and its rules are never applied. External DTD subsets are not fetched. - No XPath evaluation. To test an expression against this document, take it to the XPath Tester.
- Encoding is decided by your browser, not by the
encoding=attribute in the declaration. A file saved as Windows-1252 while declaring UTF-8 shows mangled characters here rather than the encoding error a strict server-side parser would raise. - Everything happens in one browser tab. Documents past a few megabytes make the editor sluggish before the parser struggles. Large files belong in
xmllint --noouton the command line. - The indent button rewrites whitespace between tags. Some schemas treat whitespace inside a text node as part of the data, and mixed content such as
<p>text <b>bold</b> more</p>can shift meaning. Indent for reading, then check the diff before committing the reindented file.
Once the document parses
Fixing syntax is step one. To read a large valid file, the XML Pretty Print tool gives a collapsible tree. To pull the tree apart node by node, use the XML Parser.
Moving the data elsewhere usually means a conversion: XML to JSON for a JavaScript client, and the XML Minifier when payload size on the wire is what you are cutting.
Your document never leaves the browser. Parsing, counting, and formatting all run in local JavaScript, so no request carries the content to Toolexe or anywhere else.
