XML is still everywhere
REST moved most new APIs to JSON, but XML remains the format of SOAP services, SAML assertions, RSS and Atom feeds, Maven pom.xml, .NET .csproj and web.config, Android layouts, Office Open XML internals, XSLT stylesheets and countless bank and government interfaces. Those documents usually arrive as a single minified line in a log or an HTTP trace.
The formatter here is a purpose-built XML tokenizer and printer rather than a generic HTML beautifier. It checks well-formedness (matching tags, one root element, quoted attributes, legal & usage) and reports problems with the line and column where they start, plus a hint naming the element that is probably missing its closing tag.
Using the formatter
Drop in a file (.xml, .xsd, .wsdl, .rss, .plist, .config and more are recognised) or paste the text; a leading <?xml or root element is enough for auto-detection. Ctrl/Cmd+Enter formats and Ctrl/Cmd+Shift+M minifies. Use Ctrl/Cmd+Shift+C to copy and Ctrl/Cmd+K for the palette, where you can open the Tree view or convert the document to JSON or YAML. SAML responses and SOAP payloads often carry personal data, and they stay in your browser tab during all of this.
Options
- Wrap attributes above: when an element has more than this many attributes, each goes on its own line. The default, 0, wraps only when the start tag would exceed the line width. Setting it to 2 gives the one-attribute-per-line look common in Android layouts and Spring XML.
- Empty elements: As written keeps
<tags/>and<tags></tags>as they are. Self-close <a/> shortens every empty element, while Expand <a></a> writes an explicit end tag — some older parsers and XHTML-served-as-HTML need that form. - Space before /> prints
<br />instead of<br/>. - Collapse whitespace-only elements turns an element containing only spaces and newlines into an empty element. SVG
<text>and XSLT<xsl:text>are exempt because their whitespace is output. - Keep comments when minifying: Minify removes comments by default; turn this on for files where comments carry meaning, such as license headers.
Sort keys sorts attributes alphabetically, with namespace declarations first. Indent and line width work as on every other page.
Whitespace, CDATA and entities
In XML, whitespace inside text is data. The printer only adds or removes indentation between child elements of element-only content. Three cases are printed inline and verbatim:
- Mixed content such as
<p>Total: <b>42</b> items</p>, where re-indenting would insert spaces into the text. - Elements with
xml:space="preserve"and everything inside them. - CDATA sections, which are copied exactly, including any
<or&they contain.
The XML declaration, DOCTYPE, comments and processing instructions are also written exactly as found.
Entities are never expanded. &company; declared in an internal DTD stays &company; in the output. This makes the tool immune to XXE and “billion laughs” entity attacks, and it means your document is not altered. A reference to an entity that is not declared anywhere, such as HTML’s , produces a warning that suggests the numeric form  .
Byte-order marks are removed with a note.
Examples
SOAP response from a payment gateway
The namespaced envelope is indented one level per element while the CDATA message is copied unchanged.
<?xml version="1.0" encoding="UTF-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:pay="https://api.example-bank.com/payments/v2"><soap:Header><pay:RequestId>7f3c2a90-1b4e</pay:RequestId></soap:Header><soap:Body><pay:AuthorizeResponse><pay:Status>APPROVED</pay:Status><pay:AuthCode>A81F22</pay:AuthCode><pay:Amount currency="EUR">249.00</pay:Amount><pay:Message><![CDATA[Approved <3DS frictionless> & captured]]></pay:Message></pay:AuthorizeResponse></soap:Body></soap:Envelope><?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:pay="https://api.example-bank.com/payments/v2">
<soap:Header>
<pay:RequestId>7f3c2a90-1b4e</pay:RequestId>
</soap:Header>
<soap:Body>
<pay:AuthorizeResponse>
<pay:Status>APPROVED</pay:Status>
<pay:AuthCode>A81F22</pay:AuthCode>
<pay:Amount currency="EUR">249.00</pay:Amount>
<pay:Message><![CDATA[Approved <3DS frictionless> & captured]]></pay:Message>
</pay:AuthorizeResponse>
</soap:Body>
</soap:Envelope>
Maven pom.xml dependency block
A collapsed POM becomes the familiar one-element-per-line layout, and the comment is kept in place.
<project xmlns="http://maven.apache.org/POM/4.0.0"><modelVersion>4.0.0</modelVersion><groupId>com.example.shop</groupId><artifactId>orders-service</artifactId><version>2.4.0</version><!-- runtime deps --><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.postgresql</groupId><artifactId>postgresql</artifactId><scope>runtime</scope></dependency></dependencies></project><project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.shop</groupId>
<artifactId>orders-service</artifactId>
<version>2.4.0</version>
<!-- runtime deps -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
Android layout with one attribute per line
Elements with more than two attributes get one per line, and the empty Button is shortened to a self-closing tag with a space before the slash.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"><TextView android:id="@+id/order_total" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="18sp"/><Button android:id="@+id/checkout" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/checkout"></Button></LinearLayout><LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/order_total"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp" />
<Button
android:id="@+id/checkout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/checkout" />
</LinearLayout>
RSS feed item with preserved whitespace
The xml:space=“preserve” element keeps its leading spaces and line break exactly, while its siblings are indented normally.
<rss version="2.0"><channel><title>Release notes</title><item><title>v2.4.0</title><pubDate>Mon, 14 Sep 2026 08:00:00 GMT</pubDate><description><![CDATA[<p>Faster <b>checkout</b> & refunds.</p>]]></description><pre xml:space="preserve"> npm install @shop/sdk@2.4.0
npm test</pre></item></channel></rss><rss version="2.0">
<channel>
<title>Release notes</title>
<item>
<title>v2.4.0</title>
<pubDate>Mon, 14 Sep 2026 08:00:00 GMT</pubDate>
<description><![CDATA[<p>Faster <b>checkout</b> & refunds.</p>]]></description>
<pre xml:space="preserve"> npm install @shop/sdk@2.4.0
npm test</pre>
</item>
</channel>
</rss>
Common errors and how to fix them
| Error | Cause | Fix |
|---|---|---|
Expected </item> to close <item> opened at line 4, found </channel>Explained | An element was never closed, or two closing tags are in the wrong order. XML tag names are also case-sensitive, so </Item> does not close <item>. | Add the missing closing tag where the element should end; the hint names the element that is still open. |
Unescaped '&' — it must start an entity reference like & or &Explained | A literal ampersand appears in text or an attribute, typically in a URL query string or a company name like “Smith & Sons”. | Write & instead, or wrap the text in a CDATA section. |
Only one root element is allowed — <order> follows the root element <order> that closed at line 9Explained | Two XML documents were concatenated, as happens when you copy several messages from a log. | Format one document at a time, or wrap them in a single parent element. |
Text is not allowed before the root elementExplained | Something precedes the first tag: a log prefix, an HTTP header line or stray characters from a copy. | Delete everything before the XML declaration or root element. |
Entity is not declared (only & < > " ' are built in)Explained | HTML named entities are not defined in XML. This is a warning; the document still formats. | Use the numeric reference or declare the entity in the DOCTYPE. |
The value of attribute 'qty' must be in quotesExplained | An attribute was written HTML-style without quotes, as in <item qty=2>. | Quote the value: qty=“2”. |
Frequently asked questions
Does pretty-printing change my XML data?
Only whitespace between child elements of element-only content changes. Text, mixed content, CDATA, comments and xml:space=“preserve” blocks are copied exactly, so a schema-validated document stays valid.
How do I format XML in Notepad++?
Install the XML Tools plugin from Plugins > Plugins Admin, then use Plugins > XML Tools > Pretty print. For a one-off file, pasting it here is faster and also shows a tree view.
Can the validator check my XML against an XSD schema?
Not yet. This page checks well-formedness (the rules every XML document must follow); validating against an XSD or DTD needs a schema-aware tool such as xmllint --schema.
Is it safe to paste a SAML response or SOAP message?
Yes. The document is parsed in your browser and nothing is uploaded, and because entities are never expanded, a hostile DTD cannot read files or exhaust memory.
How do I convert XML to JSON?
Use the XML to JSON converter. Attributes become @-prefixed keys and repeated elements become arrays; comments and processing instructions are dropped because JSON has no equivalent.