XML Cheat Sheet

This XML cheat sheet covers 21 sections: syntax and naming, escaping and CDATA, namespaces and encodings, XPath with real results, XSLT and its removal from browsers, XSD, DTD, RELAX NG and Schematron, parsing in six languages, XXE and billion laughs, XML and JSON, sitemaps, feeds and SVG. Check, format and query XML, convert it to JSON and generate schemas without leaving the page.

21sections
107snippets
1.0XML, unchanged since 2008
10XML tools

Updated October 14, 2026, checked against XML 1.0 Fifth Edition, XPath 3.1, XSLT 3.0, XSD 1.1, lxml 6.1 and Chrome's XSLT removal schedule. Print it for a PDF copy.

/
level

<nothing/>

Nothing matches that search. Try a shorter keyword such as xpath, namespace or escape. Several words are combined, so every one of them has to match. Keys: / focuses the search, Esc clears it, t switches the theme.

01

Toolbox & First Document

check, format and query XML, convert it to JSON, and build sitemaps, feeds and schemas

XML Toolbox: Well-Formedness Check, Format, XPath Tester, XML and JSON, Escape

core
Ctrl+Enter checks the document with your browser's own XML parser and lists the error with its line, namespaces and structure. Run XPath takes an XPath 1.0 expression on a first line starting with xpath: and the document below; prefixes declared in the document can be used in the expression. XML ↔ JSON converts in whichever direction fits the input. Escape text turns any text into XML-safe content and attribute values. Nothing is sent anywhere, and no external DTD or entity is ever loaded.
output

          
Paste an XML document, an XPath plus a document, JSON or text above, then pick an action.

Builders: Sitemap, RSS and Atom Feeds, XSD From a Sample, XSLT Stylesheet

everyday
sitemap.xml

          

A Complete Document

core

Declaration, root, elements, attributes, text

<?xml version="1.0" encoding="UTF-8"?>
<order id="8812" status="paid">
  <customer>Ana Pop</customer>
  <items>
    <item sku="LAMP-01" qty="1">Desk lamp</item>
  </items>
  <note/>
</order>

Parse it with Python's standard library

import xml.etree.ElementTree as ET

xml = """<order id="8812" status="paid">
  <customer>Ana Pop</customer>
  <items><item sku="LAMP-01" qty="1">Desk lamp</item></items>
</order>"""
root = ET.fromstring(xml)
print(root.tag, root.attrib)
print(root.find("customer").text)
for item in root.iter("item"):
    print(item.get("sku"), int(item.get("qty")), item.text)
02

Syntax Rules

elements, attributes, comments, processing instructions and whitespace

The Building Blocks

core

Every kind of node in one document

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="style.xsl"?>
<!-- a comment: not part of the data -->
<library xmlns="https://example.com/ns/library">
  <book id="b1" available="true">
    <title>Dune</title>
    <isbn/>
    <summary><![CDATA[Uses <tags> & ampersands freely]]></summary>
  </book>
</library>

Rules a Parser Enforces

core

What makes a document well formed

RuleWrongRight
one root element<a/><b/><root><a/><b/></root>
tags close in order<b><i>x</b></i><b><i>x</i></b>
end tags required<p>text<p>text</p>
attributes quoted<a id=1/><a id="1"/>
attribute names unique<a x="1" x="2"/><a x="1" y="2"/>
case matches<Name></name><name></name>
< and & escaped in texta < b & ca &lt; b &amp; c

Elements or Attributes

everyday

A rule of thumb

<!-- data as elements, metadata as attributes -->
<price currency="EUR">49.00</price>
<book id="b1" lang="en">
  <title>Dune</title>
  <author>Frank Herbert</author>
  <author>Brian Herbert</author>
</book>

Comments and Processing Instructions

everyday

What comments may not contain

import xml.etree.ElementTree as ET

for doc in ["<a><!-- fine - here --></a>", "<a><!-- not -- here --></a>", "<a><!-- ends with ---></a>"]:
    try:
        ET.fromstring(doc)
        print("ok   ", doc)
    except ET.ParseError as e:
        print("error", doc, "->", e)

Processing instructions

<?xml-stylesheet type="text/xsl" href="render.xsl"?>
<?php echo "PHP files are not XML, but they borrowed the syntax"; ?>
<?mso-application progid="Excel.Sheet"?>
03

Names & Characters

legal element names, reserved prefixes, characters and whitespace

Legal Names

core

What an element or attribute name may be

NameValid?Why
orderyes |
order-itemyeshyphens and dots are allowed after the first character
_privateyesmay start with an underscore
dc:titleyesa prefix and a local name
2fanocannot start with a digit
first namenono spaces
xmlDatayes, but avoidnames starting with xml in any case are reserved

Check a name in Python

import xml.etree.ElementTree as ET

for name in ["order-item", "_private", "2fa", "first name"]:
    try:
        ET.fromstring(f"<{name}/>")
        print(f"{name!r}: ok")
    except ET.ParseError as e:
        print(f"{name!r}: {e}")

Characters

everyday

Characters XML 1.0 forbids

RangeAllowed in XML 1.0?
tab, line feed, carriage returnyes
U+0020 to U+D7FF, U+E000 to U+FFFDyes
U+10000 to U+10FFFF (emoji and more)yes
other control characters such as U+0000, U+0008no, not even as &#8;

Whitespace

advanced

Kept, ignored or normalized

<poem xml:space="preserve">
  Line one
    indented line two
</poem>
<name>  Ana  </name>           <!-- the spaces belong to the text -->
<item code="  A1  "/>          <!-- attribute values keep them too -->
04

Escaping & CDATA

the five entities, character references and CDATA sections

The Five Predefined Entities

core

What to escape and where

CharacterEntityMust be escaped
<&lt;always, in text and attribute values
&&amp;always
>&gt;only in the sequence ]]> in text, but escaping it is always fine
"&quot;inside attribute values quoted with "
'&apos;inside attribute values quoted with '

Let the library escape for you

from xml.sax.saxutils import escape, quoteattr
import xml.etree.ElementTree as ET

print(escape("Tom & Jerry <cartoon>"))
print(quoteattr('say "hi"'))
el = ET.Element("note", title='A "quoted" & <odd> title')
el.text = "x < y && y > z"
print(ET.tostring(el, encoding="unicode"))

Character References

everyday

Any character by number

<copyright>&#169; 2026, &#xA9; 2026</copyright>
<arrow>&#8594;</arrow>
<nbsp>&#160;</nbsp>

CDATA Sections

everyday

Raw text without escaping

<script><![CDATA[
  if (a < b && b > c) { run(); }
]]></script>
<code><![CDATA[<b>bold</b>]]></code>

Text that contains ]]>

<x><![CDATA[a]]]]><![CDATA[>b]]></x>

Quotes in Attributes

everyday

Either quote style works

<a title="He said 'hi'"/>
<a title='He said "hi"'/>
<a title="He said &quot;hi&quot; and 'bye'"/>

Escaping in Other Languages

everyday

The right function per language

LanguageFunction
Pythonxml.sax.saxutils.escape(text) and quoteattr(value)
JavaScriptno built-in; set textContent or setAttribute and let XMLSerializer escape
Javabuild with DOM or StAX; Apache Commons Text StringEscapeUtils.escapeXml11
C#SecurityElement.Escape(text), or XElement and XAttribute
Goxml.EscapeText(writer, data)
PHPhtmlspecialchars($s, ENT_XML1ENT_QUOTES, 'UTF-8')
05

Well-Formed vs Valid

what parsers check, what validators check, and common parse errors

Two Levels of Correct

core

Well formed is syntax, valid is a schema

LevelChecked byMeans
well formedevery XML parserthe syntax rules are met; the document can be read
valida validator with a DTD, XSD, RELAX NG or Schematronthe document also follows a schema: which elements, in which order, with which types

Common Parse Errors

core

The messages and what they mean

import xml.etree.ElementTree as ET

bad = {
    "mismatched": "<a><b></a>",
    "unescaped &": "<a>Tom & Jerry</a>",
    "html entity": "<a>&nbsp;</a>",
    "two roots": "<a/><b/>",
    "unquoted attr": "<a id=1/>",
}
for label, doc in bad.items():
    try:
        ET.fromstring(doc)
    except ET.ParseError as e:
        print(f"{label:13} {e}")

The same errors in lxml, with more detail

from lxml import etree

try:
    etree.fromstring(b"<order><item>Lamp</order>")
except etree.XMLSyntaxError as e:
    print(e)

Errors in the Browser

everyday

DOMParser returns a parsererror document

const doc = new DOMParser().parseFromString("<order><item>Tom & Jerry</item></order>", "application/xml");
const err = doc.querySelector("parsererror");
console.log(err !== null);
console.log(err.textContent.replace(/\s+/g, " ").trim().slice(0, 120));
06

Namespaces

prefixes, default namespaces, and why XPath sees nothing

Declaring Namespaces

core

A default namespace and a prefixed one

<feed xmlns="http://www.w3.org/2005/Atom"
      xmlns:media="http://search.yahoo.com/mrss/">
  <title>Example</title>
  <entry>
    <title>First post</title>
    <media:thumbnail url="https://example.com/a.jpg"/>
  </entry>
</feed>

The Rules People Miss

core

Prefixes are local, URIs are what count

FactConsequence
the prefix is just an alias<a:x xmlns:a="urn:n"/> and <b:x xmlns:b="urn:n"/> are the same element
unprefixed attributes are in no namespacea default namespace does not apply to attributes
the default namespace applies to descendantsevery child element of feed above is an Atom element
xml: is predeclaredxml:lang and xml:space need no declaration

Namespaces in Code

core

Why find() returns nothing, and the fix

import xml.etree.ElementTree as ET

doc = ET.fromstring('<feed xmlns="http://www.w3.org/2005/Atom"><title>Example</title></feed>')
print(doc.tag)
print(doc.find("title"))
ns = {"atom": "http://www.w3.org/2005/Atom"}
print(doc.find("atom:title", ns).text)
print(doc.find("{http://www.w3.org/2005/Atom}title").text)

The same in the browser with XPath

const doc = new DOMParser().parseFromString(
  '<feed xmlns="http://www.w3.org/2005/Atom"><title>Example</title></feed>', "application/xml");
const ns = (prefix) => ({ atom: "http://www.w3.org/2005/Atom" })[prefix] || null;
console.log(doc.evaluate("count(//title)", doc, ns, XPathResult.NUMBER_TYPE, null).numberValue);
console.log(doc.evaluate("string(//atom:title)", doc, ns, XPathResult.STRING_TYPE, null).stringValue);

Namespaces in lxml XPath

everyday

Pass a prefix map

from lxml import etree

doc = etree.XML(b'<feed xmlns="http://www.w3.org/2005/Atom"><entry><title>One</title></entry><entry><title>Two</title></entry></feed>')
ns = {"a": "http://www.w3.org/2005/Atom"}
print(doc.xpath("//a:entry/a:title/text()", namespaces=ns))
print(doc.xpath("//*[local-name()='title']/text()"))

Schema Locations

advanced

Hints that point to a schema

<order xmlns="https://example.com/ns/order"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="https://example.com/ns/order order.xsd">
  ...
</order>
07

Encoding & the Declaration

UTF-8, BOMs, the encoding attribute and standalone

The XML Declaration

core

Every attribute and its default

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

Encoding Mismatches

everyday

The declaration must match the bytes

from lxml import etree

latin1 = '<?xml version="1.0" encoding="UTF-8"?><name>Zoë</name>'.encode("latin-1")
try:
    etree.fromstring(latin1)
except etree.XMLSyntaxError as e:
    print(e)

ok = '<?xml version="1.0" encoding="ISO-8859-1"?><name>Zoë</name>'.encode("latin-1")
print(etree.fromstring(ok).text)

Parse bytes, not decoded strings

etree.fromstring(response.content)          # bytes: the declaration decides the encoding
etree.fromstring(response.text)             # str with a declaration: lxml raises ValueError

Write Files With the Right Bytes

everyday

Declaration and encoding from one call

import io
import xml.etree.ElementTree as ET

root = ET.Element("name")
root.text = "Zoë"
buf = io.BytesIO()
ET.ElementTree(root).write(buf, encoding="utf-8", xml_declaration=True)
print(buf.getvalue())
08

DTD, XSD & RELAX NG

declaring structure and validating documents

XML Schema (XSD)

core

A schema for an order

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="order">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="customer" type="xs:string"/>
        <xs:element name="created" type="xs:dateTime"/>
        <xs:element name="item" maxOccurs="unbounded">
          <xs:complexType>
            <xs:simpleContent>
              <xs:extension base="xs:decimal">
                <xs:attribute name="sku" type="xs:string" use="required"/>
                <xs:attribute name="qty" type="xs:positiveInteger" default="1"/>
              </xs:extension>
            </xs:simpleContent>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
      <xs:attribute name="id" type="xs:integer" use="required"/>
      <xs:attribute name="status">
        <xs:simpleType>
          <xs:restriction base="xs:string">
            <xs:enumeration value="pending"/>
            <xs:enumeration value="paid"/>
          </xs:restriction>
        </xs:simpleType>
      </xs:attribute>
    </xs:complexType>
  </xs:element>
</xs:schema>

Validate and read the errors

from lxml import etree

schema = etree.XMLSchema(etree.parse("order.xsd"))
doc = etree.XML('<order id="x12" status="shipped"><customer>Ana</customer><created>2026-10-14</created><item>49.00</item></order>')
print(schema.validate(doc))
for err in schema.error_log:
    print(err.line, err.message)

Built-in types you will use

TypeExample values
xs:string, xs:tokenany text; token collapses whitespace
xs:integer, xs:positiveInteger, xs:decimal42, 7, 49.00
xs:booleantrue, false, 1, 0
xs:date, xs:dateTime, xs:time2026-10-14, 2026-10-14T09:12:03Z, 09:12:03
xs:anyURIhttps://example.com/a
xs:ID, xs:IDREFunique identifiers and references to them

Restrictions

everyday

Patterns, lengths and ranges

<xs:simpleType name="Sku">
  <xs:restriction base="xs:string">
    <xs:pattern value="[A-Z]{4}-\d{2}"/>
    <xs:maxLength value="7"/>
  </xs:restriction>
</xs:simpleType>
<xs:simpleType name="Quantity">
  <xs:restriction base="xs:integer">
    <xs:minInclusive value="1"/>
    <xs:maxInclusive value="999"/>
  </xs:restriction>
</xs:simpleType>

DTD

everyday

The original, inline in the document

<?xml version="1.0"?>
<!DOCTYPE note [
  <!ELEMENT note (to, from, body)>
  <!ELEMENT to (#PCDATA)>
  <!ELEMENT from (#PCDATA)>
  <!ELEMENT body (#PCDATA)>
  <!ATTLIST note priority (low|high) "low">
  <!ENTITY company "ACME Ltd.">
]>
<note><to>Ana</to><from>&company;</from><body>Hi</body></note>

RELAX NG and Schematron

advanced

RELAX NG compact syntax

element order {
  attribute id { xsd:integer },
  element customer { text },
  element item { attribute sku { text }, xsd:decimal }+
}

Schematron rules in XPath

from lxml import etree, isoschematron

rules = etree.XML("""
<schema xmlns="http://purl.oclc.org/dsdl/schematron">
  <pattern>
    <rule context="book">
      <assert test="number(price) &gt; 0">price must be positive</assert>
      <assert test="@lang = 'en' or @lang = 'ro'">unknown language</assert>
    </rule>
  </pattern>
</schema>""")
schematron = isoschematron.Schematron(rules)
print(schematron.validate(etree.XML('<book lang="fr"><price>0</price></book>')))
print(schematron.validate(etree.XML('<book lang="en"><price>9.5</price></book>')))

Validate in Other Languages

everyday

javax.xml.validation

SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(new File("order.xsd"));
Validator validator = schema.newValidator();
validator.validate(new StreamSource(new File("order.xml")));

XmlSchemaSet

var schemas = new XmlSchemaSet();
schemas.Add(null, "order.xsd");
var doc = XDocument.Load("order.xml");
doc.Validate(schemas, (sender, e) => Console.WriteLine($"{e.Severity}: {e.Message}"));

RELAX NG with lxml

from lxml import etree

rng = etree.RelaxNG(etree.XML("""
<element name="order" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
  <attribute name="id"><data type="integer"/></attribute>
  <oneOrMore><element name="item"><text/></element></oneOrMore>
</element>"""))
print(rng.validate(etree.XML('<order id="7"><item>Lamp</item></order>')))
print(rng.validate(etree.XML('<order id="seven"/>')))
print(rng.error_log.last_error.message)
09

XPath 1.0

paths, axes, predicates and functions, with results from a real document

The Sample Document

core

books.xml, used by every example in this section

<catalog>
  <book id="bk101" lang="en" year="2000">
    <title>XML Developer's Guide</title>
    <author>Gambardella, Matthew</author>
    <genre>Computer</genre>
    <price>44.95</price>
  </book>
  <book id="bk102" lang="en" year="2000">
    <title>Midnight Rain</title> <author>Ralls, Kim</author>
    <genre>Fantasy</genre> <price>5.95</price>
  </book>
  <book id="bk103" lang="ro" year="2001">
    <title>Maeve Ascendant</title> <author>Corets, Eva</author>
    <genre>Fantasy</genre> <price>5.95</price>
  </book>
  <book id="bk104" lang="en" year="2003">
    <title>Visual Studio 7: A Comprehensive Guide</title> <author>Galos, Mike</author>
    <genre>Computer</genre> <price>49.95</price>
  </book>
</catalog>

Paths

core

Select nodes by location

from lxml import etree
doc = etree.parse("books.xml")

print(doc.xpath("/catalog/book[1]/title/text()"))
print(doc.xpath("//title/text()")[-1])
print(doc.xpath("//book/@id"))
print(len(doc.xpath("//*")))
print(doc.xpath("string(//book[last()]/author)"))

Path syntax at a glance

ExpressionSelects
/catalogthe root element named catalog
//bookevery book element anywhere
book/titletitle children of book children of the context node
//book/@idevery id attribute of a book
. and ..the context node and its parent
* and @*any element, any attribute
//title|//pricethe union of both node sets, in document order

Predicates

core

Filter with conditions in square brackets

from lxml import etree
doc = etree.parse("books.xml")

print(doc.xpath("//book[price > 40]/title/text()"))
print(doc.xpath("//book[@lang='ro']/@id"))
print(doc.xpath("//book[genre='Fantasy' and @year='2001']/title/text()"))
print(doc.xpath("//book[contains(title, 'Guide')]/@id"))
print(doc.xpath("//book[not(@lang='en')]/@id"))

Axes

everyday

Move in any direction from the context node

from lxml import etree
doc = etree.parse("books.xml")

print(doc.xpath("//title[.='Midnight Rain']/following-sibling::price/text()"))
print(doc.xpath("//title[.='Midnight Rain']/parent::book/@id"))
print(doc.xpath("//book[@id='bk102']/following-sibling::book/@id"))
print(doc.xpath("//book[@id='bk103']/preceding-sibling::book[1]/@id"))
print(doc.xpath("count(//price/ancestor::*)"))

All thirteen axes

AxisDirection
child, attributeone level down (the default and @)
descendant, descendant-or-selfall levels down
parent, ancestor, ancestor-or-selfup
following-sibling, preceding-siblingsame parent, after or before
following, precedingeverything after or before in document order, excluding ancestors and descendants
selfthe node itself
namespacenamespace nodes, rarely used

Functions

core

The XPath 1.0 function library in use

from lxml import etree
doc = etree.parse("books.xml")

print(doc.xpath("count(//book)"), doc.xpath("sum(//price)"))
print(doc.xpath("round(sum(//price) div count(//price) * 100) div 100"))
print(doc.xpath("concat(//book[1]/@id, ' / ', //book[1]/@year)"))
print(doc.xpath("substring-before(//book[1]/author, ',')"))
print(doc.xpath("normalize-space('  too   many  spaces ')"))
print(doc.xpath("translate('XML Guide', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"))
print(doc.xpath("starts-with(//book[2]/title, 'Mid')"))

Function reference

GroupFunctions
node setscount(), last(), position(), id(), local-name(), name(), namespace-uri()
stringsstring(), concat(), contains(), starts-with(), substring(), substring-before(), substring-after(), string-length(), normalize-space(), translate()
numbersnumber(), sum(), floor(), ceiling(), round()
booleansboolean(), not(), true(), false(), lang()

XPath in the Browser

everyday

document.evaluate

const xml = '<catalog><book id="b1"><price>44.95</price></book><book id="b2"><price>5.95</price></book></catalog>';
const doc = new DOMParser().parseFromString(xml, "application/xml");
const it = doc.evaluate("//book[price < 10]/@id", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (let i = 0; i < it.snapshotLength; i++) console.log(it.snapshotItem(i).value);
console.log(doc.evaluate("sum(//price)", doc, null, XPathResult.NUMBER_TYPE, null).numberValue);

XPath in DevTools and Selenium

$x("//button[contains(., 'Submit')]")
driver.find_element(By.XPATH, "//input[@name='email']")
page.locator("xpath=//table//tr[td[1]='Ana']")

Safe Values in XPath

core

Variables instead of string building

from lxml import etree
doc = etree.parse("books.xml")

user_input = "bk103' or '1'='1"
print(doc.xpath("//book[@id=$id]/title/text()", id=user_input))
print(doc.xpath("//book[@id=$id]/title/text()", id="bk103"))

Recipes

everyday

Expressions people search for

NeedXPath 1.0
the last child element/*/*[last()]
the second book(//book)[2]
elements with an attribute//book[@lang]
elements without a child//book[not(price)]
non-empty text//title[normalize-space()]
exact class token in HTML//div[contains(concat(' ', normalize-space(@class), ' '), ' card ')]
first of each genre//book[not(genre = preceding-sibling::book/genre)]
case-insensitive match//book[translate(@lang, 'EN', 'en') = 'en']
10

XPath 2.0 to 4.0

sequences, types, if and for, regex, maps and arrays

What 2.0 and 3.1 Added

everyday

Expressions you cannot write in XPath 1.0

lower-case(//title[1])
//book[matches(title, '^[A-M]')]/@id
string-join(//book/@id, ', ')
for $b in //book return $b/title || ' (' || $b/@year || ')'
if (count(//book) > 3) then 'big' else 'small'
let $avg := avg(//price) return //book[price > $avg]/title
//price ! xs:decimal(.) => sum()
map { 'count': count(//book), 'genres': array { distinct-values(//genre) } }

XPath 4.0 is still a draft

SpecStatus in October 2026
XPath 3.1, XSLT 3.0, XQuery 3.1W3C Recommendations since 2017
XPath 4.0, XSLT 4.0, XQuery 4.0drafts of the W3C QT4 Community Group
11

XSLT

templates, the identity transform, sorting and grouping, and XSLT in browsers

A First Stylesheet

core

Books to an HTML list

from lxml import etree

xslt = etree.XML("""
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html" indent="yes"/>
  <xsl:template match="/catalog">
    <ul>
      <xsl:for-each select="book[genre='Fantasy']">
        <xsl:sort select="title"/>
        <li><xsl:value-of select="title"/> (<xsl:value-of select="@year"/>)</li>
      </xsl:for-each>
    </ul>
  </xsl:template>
</xsl:stylesheet>""")
transform = etree.XSLT(xslt)
print(str(transform(etree.parse("books.xml"))).strip())

The Identity Transform

core

Copy everything, change one thing

from lxml import etree

xslt = etree.XSLT(etree.XML("""
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="@*|node()">
    <xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy>
  </xsl:template>
  <xsl:template match="price"/>
  <xsl:template match="book/@year">
    <xsl:attribute name="published"><xsl:value-of select="."/></xsl:attribute>
  </xsl:template>
</xsl:stylesheet>"""))
doc = etree.XML('<catalog><book id="b1" year="2000"><title>Dune</title><price>9</price></book></catalog>')
print(etree.tostring(xslt(doc)).decode())

Control Flow

everyday

Conditions, variables and parameters

<xsl:param name="currency" select="'EUR'"/>
<xsl:variable name="total" select="sum(//price)"/>

<xsl:template match="book">
  <xsl:choose>
    <xsl:when test="price &gt; 40">expensive</xsl:when>
    <xsl:when test="price &gt; 10">regular</xsl:when>
    <xsl:otherwise>bargain</xsl:otherwise>
  </xsl:choose>
  <xsl:if test="@lang != 'en'"> (translated)</xsl:if>
</xsl:template>

XSLT 2.0 and 3.0

advanced

Grouping and JSON output

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="json" indent="yes"/>
  <xsl:template match="/catalog">
    <xsl:map>
      <xsl:for-each-group select="book" group-by="genre">
        <xsl:map-entry key="current-grouping-key()" select="array { current-group()/title/string() }"/>
      </xsl:for-each-group>
    </xsl:map>
  </xsl:template>
</xsl:stylesheet>

XSLT in Browsers

everyday

Being removed from Chrome

Chrome versionDateXSLT
143December 2025deprecated, with console warnings
158November 17, 2026stops working on Stable, except with an origin trial or enterprise policy
176August 17, 2027removed for everyone

Grouping in XSLT 1.0

advanced

Muenchian grouping with xsl:key

from lxml import etree

xslt = etree.XSLT(etree.XML("""
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:key name="by-genre" match="book" use="genre"/>
  <xsl:template match="/catalog">
    <xsl:for-each select="book[generate-id() = generate-id(key('by-genre', genre)[1])]">
      <xsl:value-of select="genre"/>: <xsl:value-of select="count(key('by-genre', genre))"/>
      <xsl:text>&#10;</xsl:text>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>"""))
print(str(xslt(etree.parse("books.xml"))).strip())

Parameters From Code

everyday

Pass values into a stylesheet

from lxml import etree

xslt = etree.XSLT(etree.XML("""
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:param name="max" select="10"/>
  <xsl:template match="/">
    <xsl:value-of select="count(//book[price &lt; $max])"/>
  </xsl:template>
</xsl:stylesheet>"""))
doc = etree.parse("books.xml")
print(xslt(doc), xslt(doc, max="50"))
12

XQuery

FLWOR queries over XML documents and databases

FLWOR

advanced

for, let, where, order by, return

for $b in doc("books.xml")/catalog/book
let $price := xs:decimal($b/price)
where $b/genre = "Computer"
order by $price descending
return <cheap-or-not title="{$b/title}">{ if ($price > 45) then "expensive" else "fine" }</cheap-or-not>
13

Parsing Models

DOM, SAX, pull parsers and streaming large files

Four Ways to Read XML

core

Pick by file size and access pattern

ModelHow it worksMemoryUse for
DOM / treethe whole document becomes a treeabout 3 to 10 times the file sizedocuments up to tens of MB, random access, editing
SAXcallbacks for each start tag, text and end tagconstanthuge files, one pass, simple extraction
pull (StAX, XmlReader)your code asks for the next eventconstanthuge files with easier control flow than SAX
streaming tree (iterparse)builds elements, you discard them after usesmallhuge files when you still want element objects

Stream a Huge File

everyday

iterparse and clear

import xml.etree.ElementTree as ET

total, count = 0.0, 0
for event, el in ET.iterparse("books.xml", events=("end",)):
    if el.tag == "book":
        total += float(el.findtext("price"))
        count += 1
        el.clear()
print(count, round(total, 2))

SAX

everyday

Callbacks, constant memory

import xml.sax

class Prices(xml.sax.ContentHandler):
    def __init__(self):
        super().__init__()
        self.in_price, self.total = False, 0.0
    def startElement(self, name, attrs):
        self.in_price = name == "price"
    def characters(self, content):
        if self.in_price:
            self.total += float(content)
    def endElement(self, name):
        self.in_price = False

handler = Prices()
xml.sax.parse("books.xml", handler)
print(round(handler.total, 2))
14

XML in Code

parsing, building and querying XML in six languages

Read a Value

core

ElementTree and lxml

import xml.etree.ElementTree as ET
from lxml import etree

tree = ET.parse("books.xml")
print(tree.getroot().find("book[@id='bk102']/title").text)
print(etree.parse("books.xml").xpath("string(//book[@id='bk102']/title)"))

DOMParser in browsers

const xml = '<catalog><book id="bk102"><title>Midnight Rain</title></book></catalog>';
const doc = new DOMParser().parseFromString(xml, "application/xml");
const err = doc.querySelector("parsererror");
if (err) throw new Error(err.textContent);
console.log(doc.querySelector("book[id='bk102'] > title").textContent);

DOM and XPath in the JDK

DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
Document doc = f.newDocumentBuilder().parse(new File("books.xml"));
XPath xpath = XPathFactory.newInstance().newXPath();
String title = xpath.evaluate("//book[@id='bk102']/title", doc);

LINQ to XML

var doc = XDocument.Load("books.xml");
var title = doc.Descendants("book")
    .First(b => (string)b.Attribute("id") == "bk102")
    .Element("title")?.Value;
var cheap = doc.Descendants("book").Where(b => (decimal)b.Element("price") < 10).Count();

encoding/xml with struct tags

type Book struct {
	ID    string  `xml:"id,attr"`
	Title string  `xml:"title"`
	Price float64 `xml:"price"`
}
type Catalog struct {
	Books []Book `xml:"book"`
}

var c Catalog
if err := xml.Unmarshal(data, &c); err != nil {
	return err
}

SimpleXML

$catalog = simplexml_load_file('books.xml');
foreach ($catalog->book as $book) {
    echo $book['id'], ': ', $book->title, PHP_EOL;
}
$titles = $catalog->xpath("//book[price < 10]/title");

Build a Document

core

ElementTree with indentation

import xml.etree.ElementTree as ET

order = ET.Element("order", id="8812")
ET.SubElement(order, "customer").text = "Ana & Co"
item = ET.SubElement(order, "item", sku="LAMP-01", qty="1")
item.text = "49.00"
ET.indent(order)
print(ET.tostring(order, encoding="unicode"))

DOM and XMLSerializer

const doc = document.implementation.createDocument(null, "order", null);
const order = doc.documentElement;
order.setAttribute("id", "8812");
const c = doc.createElement("customer");
c.textContent = "Ana & Co";
order.appendChild(c);
console.log(new XMLSerializer().serializeToString(doc));

LINQ to XML

var order = new XElement("order", new XAttribute("id", 8812),
    new XElement("customer", "Ana & Co"),
    new XElement("item", new XAttribute("sku", "LAMP-01"), 49.00m));
order.Save("order.xml");

Change a Document

everyday

Edit, remove and add with lxml

from lxml import etree

doc = etree.parse("books.xml")
root = doc.getroot()
for price in root.iter("price"):
    price.text = f"{float(price.text) * 1.1:.2f}"
for book in root.xpath("//book[genre='Fantasy']"):
    book.getparent().remove(book)
root.append(etree.fromstring('<book id="bk105"><title>New</title></book>'))
print([b.get("id") for b in root], root.findtext("book/price"))
15

XML Security

XXE, billion laughs, XPath injection and safe parser settings

XXE: External Entities

core

The attack

<?xml version="1.0"?>
<!DOCTYPE order [
  <!ENTITY secret SYSTEM "file:///etc/passwd">
]>
<order><customer>&secret;</customer></order>

Modern defaults refuse it

import xml.etree.ElementTree as ET
from lxml import etree

doc = b'<?xml version="1.0"?><!DOCTYPE r [<!ENTITY x SYSTEM "file:///etc/passwd">]><r>&x;</r>'
for name, parse in [("ElementTree", lambda d: ET.fromstring(d)), ("lxml", lambda d: etree.fromstring(d))]:
    try:
        parse(doc)
    except Exception as e:
        print(f"{name}: {e}")

Billion Laughs

core

Entities that expand exponentially

<!DOCTYPE lolz [
  <!ENTITY lol "lol">
  <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
  <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
  <!-- ... up to lol9 -->
]>
<lolz>&lol9;</lolz>

Today's parsers refuse it

import xml.etree.ElementTree as ET
from lxml import etree

ents = '<!ENTITY a "aaaaaaaaaa">' + "".join(
    f'<!ENTITY {n} "{("&" + p + ";") * 10}">' for p, n in zip("abcdefg", "bcdefgh"))
doc = f'<?xml version="1.0"?><!DOCTYPE l [{ents}]><l>&h;</l>'
for name, parse in [("ElementTree", ET.fromstring), ("lxml", lambda d: etree.fromstring(d.encode()))]:
    try:
        parse(doc)
    except Exception as e:
        print(f"{name}: {e}")

Safe Parser Settings

core

One line per language

LanguageSetting
Pythonuse defusedxml, or lxml with resolve_entities=False and no_network=True
Javafactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)
.NETXmlReaderSettings.DtdProcessing = DtdProcessing.Prohibit (the default for XmlReader)
PHP 8safe by default; never pass LIBXML_NOENT or LIBXML_DTDLOAD for untrusted input
Goencoding/xml does not resolve external entities
browsersDOMParser never loads external entities or DTDs

XSLT and XPath Risks

advanced

Untrusted stylesheets are code

RiskMitigation
document() and xsl:include read files and URLslxml: XSLTAccessControl.DENY_ALL; Saxon: restrict URI resolvers
extension functions run codedo not enable them for untrusted stylesheets
XPath built from inputuse variables, as shown in the XPath section
huge or deep documentscap input size; lxml huge_tree=False is the default
16

XML and JSON

conversion conventions, pitfalls and when to choose which

Comparison

core

The same data, two formats

XMLJSON
attributes and elementsboth, plus mixed contentonly keys and values
schemasXSD, RELAX NG, Schematron, matureJSON Schema
commentsyesno
namespacesyesno
data typestext unless a schema says otherwisestring, number, boolean, null, array, object
sizelarger, end tags repeat namessmaller
typical usedocuments, publishing, finance, government, configweb APIs and app data

Conversion Conventions

everyday

Attributes and repeated elements need a rule

{
  "catalog": {
    "book": [
      {"@id": "bk101", "title": "XML Developer's Guide", "price": "44.95"},
      {"@id": "bk102", "title": "Midnight Rain", "price": "5.95"}
    ]
  }
}

What Gets Lost

everyday

Round trips are not lossless

XML featureTypical JSON result
attribute vs child elementboth become keys; the difference is a naming convention
one vs many childrenobject vs array, depending on the count
order of different childrenlost unless you use arrays everywhere
mixed contenttext pieces merge or drop
comments and processing instructionsdropped
namespaceskept as prefixed names or dropped
17

XML and HTML

XHTML, why HTML is not XML, and parsing HTML with XML tools

Why HTML Is Not XML

core

The same markup, two parsers

from lxml import etree, html

page = "<p>One<br>Two<p>Three &nbsp; four"
try:
    etree.fromstring(page)
except etree.XMLSyntaxError as e:
    print("XML parser:", e)
tree = html.fromstring(page)
print("HTML parser:", [p.text_content() for p in tree.xpath("//p")])

XHTML

everyday

HTML written as XML

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
  <head><title>Page</title></head>
  <body>
    <p>Line one<br/>line two</p>
    <img src="a.png" alt="A"/>
  </body>
</html>

Scraping With XPath

everyday

lxml.html and XPath on a real page structure

from lxml import html

doc = html.fromstring("""
<table id="prices">
  <tr><td>Lamp</td><td class="price">49.00</td></tr>
  <tr><td>Bulb</td><td class="price">3.50</td></tr>
</table>""")
for row in doc.xpath('//table[@id="prices"]//tr'):
    name, price = row.xpath("td[1]/text()")[0], row.xpath('td[@class="price"]/text()')[0]
    print(name, float(price))
18

XML Formats You Meet

sitemaps, feeds, SVG, SOAP, Office files and build files

Sitemaps

core

A sitemap with two URLs

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://example.com/</loc>
    <lastmod>2026-10-14</lastmod>
  </url>
  <url>
    <loc>https://example.com/pricing</loc>
    <lastmod>2026-09-30T08:00:00+00:00</lastmod>
  </url>
</urlset>

RSS and Atom

everyday

One item in each

<rss version="2.0">
  <channel>
    <title>Example Blog</title>
    <link>https://example.com/blog</link>
    <description>Notes</description>
    <item>
      <title>Parsing XML safely</title>
      <link>https://example.com/blog/xml-safely</link>
      <guid isPermaLink="true">https://example.com/blog/xml-safely</guid>
      <pubDate>Mon, 12 Oct 2026 08:00:00 GMT</pubDate>
    </item>
  </channel>
</rss>

<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Example Blog</title>
  <id>https://example.com/blog</id>
  <updated>2026-10-12T08:00:00Z</updated>
  <entry>
    <title>Parsing XML safely</title>
    <id>https://example.com/blog/xml-safely</id>
    <updated>2026-10-12T08:00:00Z</updated>
    <link href="https://example.com/blog/xml-safely"/>
  </entry>
</feed>

SVG

everyday

Vector graphics are XML

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 40" width="120" height="40">
  <rect x="1" y="1" width="118" height="38" rx="8" fill="#0b1220" stroke="#4aa8ff"/>
  <text x="60" y="26" text-anchor="middle" font-family="sans-serif" font-size="16" fill="#e6edf7">XML</text>
</svg>

SOAP

advanced

A SOAP 1.2 request

POST /StockService HTTP/1.1
Content-Type: application/soap+xml; charset=utf-8

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
  <soap:Body>
    <GetPrice xmlns="https://example.com/stock">
      <Symbol>ACME</Symbol>
    </GetPrice>
  </soap:Body>
</soap:Envelope>

Files That Are Secretly XML

everyday

Formats built on XML

FileWhat is inside
.docx, .xlsx, .pptxa ZIP of XML parts (Office Open XML, ISO/IEC 29500)
.odt, .odsa ZIP of XML parts (OpenDocument)
.epuba ZIP with XHTML and XML metadata
pom.xmlMaven build configuration
.csproj, .vbprojMSBuild projects
AndroidManifest.xml, layout filesAndroid apps
.plistmacOS and iOS settings, XML or binary
.gpx, .kmlGPS tracks and map data
.xlifftranslation files

Build Files

everyday

A minimal Maven pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>orders</artifactId>
  <version>1.0.0</version>
  <properties>
    <maven.compiler.release>21</maven.compiler.release>
  </properties>
</project>

An Android layout

<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/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/app_name" />
</LinearLayout>
19

Tools & Command Line

xmllint, xmlstarlet, Saxon and editor support

xmllint

core

libxml2's command-line tool

xmllint --noout file.xml                      # well-formedness check, silent when fine
xmllint --format file.xml > pretty.xml        # pretty print
xmllint --xpath "//book[price>40]/title/text()" books.xml
xmllint --noout --schema order.xsd order.xml  # validate against XSD
xmllint --noout --relaxng order.rng order.xml
xmllint --noout --valid file.xml              # validate against the DOCTYPE's DTD

The same jobs in Python, no extra tools

import xml.dom.minidom

ugly = '<order id="1"><customer>Ana</customer><item sku="L1">49.00</item></order>'
print(xml.dom.minidom.parseString(ugly).toprettyxml(indent="  "))

xmlstarlet and Saxon

everyday

Query, edit and transform from the shell

xmlstarlet sel -t -v "//book[@id='bk102']/title" -n books.xml
xmlstarlet ed -u "//book[@id='bk102']/price" -v "6.95" books.xml
xmlstarlet ed -d "//book[genre='Fantasy']" books.xml
xsltproc style.xsl books.xml > out.html
java -jar saxon-he.jar -s:books.xml -xsl:style.xsl -o:out.html

Editors

everyday

Formatting and validation while you type

EditorExtension
VS CodeXML by Red Hat (redhat.vscode-xml): format, validate against XSD and DTD, completion
Notepad++XML Tools plugin: pretty print, check syntax, validate, XPath
JetBrains IDEsbuilt in: XSD and DTD aware completion and validation
Oxygen XML Editorcommercial, the reference tool for XSLT, XQuery and schemas

Canonical XML

advanced

Compare documents, sign them

import xml.etree.ElementTree as ET

a = '<order   status="paid" id="1"><item/></order>'
b = '<order id="1" status="paid"><item></item></order>'
print(ET.canonicalize(a) == ET.canonicalize(b))
print(ET.canonicalize(a))

Check Many Files in CI

everyday

Fail the build on any broken file

find . -name "*.xml" -print0 | xargs -0 -n1 xmllint --noout
xmllint --noout --schema schema.xsd config/*.xml
20

Troubleshooting

the errors that cost the most time and how to fix them

Errors and Fixes

core

Messages from Java, Python, .NET and browsers

MessageUsual cause
Content is not allowed in prologtext, a BOM in the wrong encoding, or HTML before the first tag
XML declaration allowed only at the start of the documentblank line or space before <?xml
Invalid byte 1 of 1-byte UTF-8 sequencethe file is Latin-1 or Windows-1252 but declared or assumed UTF-8
Entity 'nbsp' not definedan HTML entity; use &#160;
The prefix "x" for element "x:y" is not boundmissing xmlns:x declaration
junk after document elementtwo root elements, or text after the root
XPath returns nothinga default namespace on the elements

See the hidden characters

data = b'\xef\xbb\xbf\n<?xml version="1.0"?><a/>'
print(data[:6])
from lxml import etree
try:
    etree.fromstring(data)
except etree.XMLSyntaxError as e:
    print(e)

Find the Real Encoding

everyday

Look at the bytes

file data.xml
head -c 64 data.xml | xxd
iconv -f WINDOWS-1252 -t UTF-8 data.xml > data.utf8.xml
21

Versions & Specs

XML, namespaces, schemas, XPath, XSLT and XQuery versions

The Recommendations

core

What is current in October 2026

SpecCurrent versionSince
XML1.0 Fifth EditionNovember 2008
XML 1.1Second Edition, rarely used2006
Namespaces in XML1.0 Third EditionDecember 2009
XML Schema (XSD)1.1, with 1.0 still the most implementedApril 2012
XPath3.1March 2017
XSLT3.0June 2017
XQuery3.1March 2017
RELAX NGISO/IEC 19757-22003, updated 2008

Where It Came From

advanced

A short history

YearEvent
1986SGML becomes ISO 8879; XML is a simplified subset of it
1998XML 1.0 becomes a W3C Recommendation in February
1999Namespaces, XPath 1.0 and XSLT 1.0
2001XML Schema 1.0
2006JSON is described in RFC 4627 and starts replacing XML in web APIs
2007XPath 2.0, XSLT 2.0 and XQuery 1.0
2017XPath 3.1, XSLT 3.0 and XQuery 3.1
2026Chrome 158 is scheduled to remove XSLT from the browser on November 17

What Is an XML Cheat Sheet

An XML cheat sheet is a compact reference to the syntax of the Extensible Markup Language and the technologies around it: escaping, namespaces, XPath queries, XSLT transformations and XSD schemas. People also look for an XPath cheat sheet or an XML syntax reference; most of them are fixing a parse error, writing a query against a document they did not design, or generating a sitemap or feed.

This cheat sheet covers 21 sections and 107 snippets. The XPath, XSLT, schema and security examples were run with lxml 6.1 and Python 3.14, and the browser examples in Microsoft Edge, so the output under each one is what it really printed on October 14, 2026. Rows that need XPath 3.1, XSLT 3.0 or XQuery carry a chip, and the 1.0 only button hides them.

The toolbox at the top checks whether a document is well formed using your browser's own XML parser and points at the error, pretty prints and minifies, runs XPath 1.0 expressions with namespace support, converts between XML and JSON, and escapes text for content, attributes and CDATA. The builders write sitemaps, RSS and Atom feeds, a first-draft XSD inferred from a sample document and common XSLT stylesheets. Everything runs in your browser; no external DTD or entity is ever loaded.

How It Differs From the Specifications

The W3C specifications define XML precisely but run to hundreds of pages; this page shows the forms you actually type, next to the errors they cause when they go wrong: the unescaped ampersand, the default namespace that makes XPath return nothing, the encoding declaration that does not match the bytes. For the data format that replaced XML in most web APIs, see the JSON cheat sheet; for configuration files, the YAML cheat sheet.


What This XML Cheat Sheet Covers

The 21 sections go from the first element to schema validation and security, and each link jumps to that section above.


XML Quick Reference

The forms people look up most often, one line each.

TaskXML
Declaration<?xml version="1.0" encoding="UTF-8"?>
Element<title>Dune</title>
Empty element<br/>
Attribute<book id="b1">
Comment<!-- note -->
Escape < and &&lt; and &amp;
Raw text<![CDATA[ a < b ]]>
Any character&#169; or &#xA9;
Default namespacexmlns="https://example.com/ns"
Prefixed namespacexmlns:dc="http://purl.org/dc/elements/1.1/"
Keep whitespacexml:space="preserve"
Every element named book//book
An attribute//book/@id
Filter//book[price > 30]
Text contains//book[contains(title, 'XML')]
Countcount(//book)
Validate against XSDxmllint --noout --schema s.xsd f.xml
Pretty printxmllint --format f.xml

What XML Is

XML, the Extensible Markup Language, is a W3C standard from 1998 for representing structured data and documents as text. It is a simplified subset of SGML: elements in angle brackets form a tree, attributes add named values, and the vocabulary is entirely up to you, which is the extensible part. XML 1.0 has been stable since its fifth edition in 2008, so documents written decades ago still parse unchanged.

Around the core language grew a family of standards: namespaces to mix vocabularies, XPath to address parts of a document, XSLT to transform it, XSD and RELAX NG to define its structure, and XQuery to query collections of documents. Web APIs moved to JSON in the late 2000s, but XML remains the backbone of document formats, publishing, enterprise integration and many industry standards.

You meet XML in sitemaps and RSS feeds, in SVG images, inside .docx and .xlsx files, in build files such as Maven's pom.xml and .NET project files, in Android layouts, in SOAP services and in standards for e-invoicing, banking and healthcare. Tools such as the XML to XSD converter and the CSV to XML converter on this site handle the most common conversions.

Where It Fits

Choose XML when documents mix text and markup, when you need namespaces, comments or strict schema validation, or when an existing standard already uses it. For new web APIs and app data, JSON is simpler and smaller; for hand-edited configuration, YAML or TOML are easier to read. Converting between them is routine, and the toolbox above handles XML and JSON in both directions.


How an XML Document Is Processed

The steps between bytes on disk and data in your program explain most XML errors.

  1. The parser detects the encoding from the byte order mark or the XML declaration and decodes the bytes into characters.
  2. It reads the optional DOCTYPE and, depending on settings, expands entities; safe settings refuse external entities.
  3. It checks well-formedness while reading tags, attributes and text, and stops with a fatal error at the first violation.
  4. It resolves namespace prefixes, so every element and attribute gets an expanded name of namespace URI plus local name.
  5. It hands the result to your code as a tree (DOM), as events (SAX or a pull parser), or as objects through a binding library.
  6. Optionally, a validator checks the result against an XSD, RELAX NG or Schematron schema and reports every violation.

Common XML Mistakes

The same problems cause most XML support tickets.

In Documents

  • An unescaped & or < in text, often from a company name or a URL with query parameters.
  • HTML entities such as &nbsp; that XML does not define.
  • A file saved as Windows-1252 but declared, or assumed, as UTF-8.
  • A blank line or space before the XML declaration.
  • Control characters copied from databases or spreadsheets.

In Code

  • Building XML with string concatenation instead of a library that escapes values.
  • XPath that ignores a default namespace and silently matches nothing.
  • Parsers that resolve external entities on untrusted input, the XXE vulnerability.
  • Loading multi-gigabyte files into a DOM instead of streaming them.
  • Relying on browser XSLT, which Chrome is removing in November 2026.

How to Work With XML Well

Use the Tools, Not Strings

Let a library build, escape and parse XML, validate documents against a schema at system boundaries, and keep XPath expressions namespace aware. Format files consistently in your editor; on Windows, the guide to formatting XML in Notepad++ shows the XML Tools plugin, and VS Code's XML extension does the same.

Then Automate the Checks

Add well-formedness and schema validation to continuous integration for every XML file you ship, from sitemaps to configuration, and test parsers with hostile input such as entity bombs as part of your security testing. The Python cheat sheet and Java cheat sheet cover the languages most XML processing code is written in.


How This XML Cheat Sheet Is Maintained

This page is written and maintained by Bogdan Sandu for TMS Outsource, a software development company. It was checked on October 14, 2026 against the XML 1.0 Fifth Edition specification, Namespaces in XML, XPath 3.1, XSLT 3.0, XSD 1.1, the QT4 Community Group drafts and Chrome's published XSLT removal schedule. The output of the examples was produced by running them with lxml 6.1 on libxml2 2.11, Python 3.14 and Microsoft Edge. If a snippet is wrong or out of date, the date in the byline shows when it was last reviewed.


XML Questions People Actually Ask

The questions developers search for most about XML, answered in a few sentences each.

What is XML used for?

XML, the Extensible Markup Language, stores and exchanges structured data and documents in a text format both people and programs can read. It is used for sitemaps and RSS feeds, SVG graphics, Office files such as .docx, configuration files such as Maven's pom.xml, SOAP web services, publishing, and industry formats in finance, healthcare and government.

What is the difference between well-formed and valid XML?

Well-formed XML follows the syntax rules: one root element, properly nested and closed tags, quoted attributes and escaped special characters. Valid XML is well formed and also follows a schema, such as a DTD, XSD or RELAX NG grammar, that says which elements and attributes are allowed and in which order.

How do I escape special characters in XML?

Write < as &lt; and & as &amp; in text and attribute values; write " as &quot; inside attribute values in double quotes. > can be written as &gt;, and ' as &apos;. Any other character can appear directly in a UTF-8 document or as a numeric character reference such as &#169;.

What is XPath?

XPath is a language for selecting parts of an XML document with path expressions such as //book[price > 30]/title. XPath 1.0 runs in browsers, lxml and most libraries; XPath 3.1 adds sequences, regular expressions, maps and arrays and is implemented by processors such as Saxon.

Why does my XPath return nothing?

Usually because the elements are in a default namespace declared with xmlns="...". In XPath 1.0 an unprefixed name matches only elements in no namespace, so bind a prefix to the namespace URI in your code and write //x:title instead of //title.

What is XSLT?

XSLT is a language for transforming XML into other XML, HTML or text with templates that match parts of the input. XSLT 1.0 runs in libxslt, lxml and browsers; XSLT 3.0 needs Saxon. Chrome 158 is scheduled to stop running XSLT in the browser on November 17, 2026.

What is an XXE attack?

XML External Entity injection abuses a DOCTYPE that declares an external entity, so a parser that resolves it reads a local file or an internal URL into the document. Prevent it by disabling DTD processing or external entities in the parser; modern defaults in Python, lxml, .NET, PHP 8 and browsers already refuse them, while Java parsers need an explicit setting.

Should I use XML or JSON?

JSON is simpler and smaller, and it is the default for web APIs. XML is the better fit for documents with mixed content, for formats that need namespaces, comments or mature schema validation, and wherever an industry standard already uses it.

Is this XML cheat sheet up to date?

Yes. It was checked on October 14, 2026 against XML 1.0 Fifth Edition, Namespaces in XML 1.0, XPath 3.1, XSLT 3.0, XSD 1.1, the QT4 drafts and Chrome's XSLT removal schedule. The example outputs come from real runs with lxml 6.1, Python 3.14 and Microsoft Edge.

Can I download this XML cheat sheet as a PDF?

Yes. The page has a print stylesheet, so your browser's print command with Save as PDF produces a clean copy with every output and explanation expanded and the navigation left out. The print core button prints only the core snippets for a short reference.