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.
<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.
Toolbox & First Document
check, format and query XML, convert it to JSON, and build sitemaps, feeds and schemas
Builders: Sitemap, RSS and Atom Feeds, XSD From a Sample, XSLT Stylesheet
A Complete Document
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)
Syntax Rules
elements, attributes, comments, processing instructions and whitespace
The Building Blocks
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
What makes a document well formed
| Rule | Wrong | Right |
|---|---|---|
| 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 text | a < b & c | a < b & c |
Elements or Attributes
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
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"?>
Names & Characters
legal element names, reserved prefixes, characters and whitespace
Legal Names
What an element or attribute name may be
| Name | Valid? | Why |
|---|---|---|
| order | yes | | |
| order-item | yes | hyphens and dots are allowed after the first character |
| _private | yes | may start with an underscore |
| dc:title | yes | a prefix and a local name |
| 2fa | no | cannot start with a digit |
| first name | no | no spaces |
| xmlData | yes, but avoid | names 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
Characters XML 1.0 forbids
| Range | Allowed in XML 1.0? |
|---|---|
| tab, line feed, carriage return | yes |
| U+0020 to U+D7FF, U+E000 to U+FFFD | yes |
| U+10000 to U+10FFFF (emoji and more) | yes |
| other control characters such as U+0000, U+0008 | no, not even as  |
Whitespace
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 -->
Escaping & CDATA
the five entities, character references and CDATA sections
The Five Predefined Entities
What to escape and where
| Character | Entity | Must be escaped |
|---|---|---|
| < | < | always, in text and attribute values |
| & | & | always |
| > | > | only in the sequence ]]> in text, but escaping it is always fine |
| " | " | inside attribute values quoted with " |
| ' | ' | 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
Any character by number
<copyright>© 2026, © 2026</copyright> <arrow>→</arrow> <nbsp> </nbsp>
CDATA Sections
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
Either quote style works
<a title="He said 'hi'"/> <a title='He said "hi"'/> <a title="He said "hi" and 'bye'"/>
Escaping in Other Languages
The right function per language
| Language | Function | |
|---|---|---|
| Python | xml.sax.saxutils.escape(text) and quoteattr(value) | |
| JavaScript | no built-in; set textContent or setAttribute and let XMLSerializer escape | |
| Java | build with DOM or StAX; Apache Commons Text StringEscapeUtils.escapeXml11 | |
| C# | SecurityElement.Escape(text), or XElement and XAttribute | |
| Go | xml.EscapeText(writer, data) | |
| PHP | htmlspecialchars($s, ENT_XML1 | ENT_QUOTES, 'UTF-8') |
Well-Formed vs Valid
what parsers check, what validators check, and common parse errors
Two Levels of Correct
Well formed is syntax, valid is a schema
| Level | Checked by | Means |
|---|---|---|
| well formed | every XML parser | the syntax rules are met; the document can be read |
| valid | a validator with a DTD, XSD, RELAX NG or Schematron | the document also follows a schema: which elements, in which order, with which types |
Common Parse Errors
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> </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
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));
Namespaces
prefixes, default namespaces, and why XPath sees nothing
Declaring Namespaces
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
Prefixes are local, URIs are what count
| Fact | Consequence |
|---|---|
| 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 namespace | a default namespace does not apply to attributes |
| the default namespace applies to descendants | every child element of feed above is an Atom element |
| xml: is predeclared | xml:lang and xml:space need no declaration |
Namespaces in Code
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
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
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>
Encoding & the Declaration
UTF-8, BOMs, the encoding attribute and standalone
The XML Declaration
Every attribute and its default
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
Encoding Mismatches
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
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())
DTD, XSD & RELAX NG
declaring structure and validating documents
XML Schema (XSD)
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
| Type | Example values |
|---|---|
| xs:string, xs:token | any text; token collapses whitespace |
| xs:integer, xs:positiveInteger, xs:decimal | 42, 7, 49.00 |
| xs:boolean | true, false, 1, 0 |
| xs:date, xs:dateTime, xs:time | 2026-10-14, 2026-10-14T09:12:03Z, 09:12:03 |
| xs:anyURI | https://example.com/a |
| xs:ID, xs:IDREF | unique identifiers and references to them |
Restrictions
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
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
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) > 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
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)
XPath 1.0
paths, axes, predicates and functions, with results from a real document
The Sample Document
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
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
| Expression | Selects |
|---|---|
| /catalog | the root element named catalog |
| //book | every book element anywhere |
| book/title | title children of book children of the context node |
| //book/@id | every id attribute of a book |
| . and .. | the context node and its parent |
| * and @* | any element, any attribute |
| //title|//price | the union of both node sets, in document order |
Predicates
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
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
| Axis | Direction |
|---|---|
| child, attribute | one level down (the default and @) |
| descendant, descendant-or-self | all levels down |
| parent, ancestor, ancestor-or-self | up |
| following-sibling, preceding-sibling | same parent, after or before |
| following, preceding | everything after or before in document order, excluding ancestors and descendants |
| self | the node itself |
| namespace | namespace nodes, rarely used |
Functions
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
| Group | Functions |
|---|---|
| node sets | count(), last(), position(), id(), local-name(), name(), namespace-uri() |
| strings | string(), concat(), contains(), starts-with(), substring(), substring-before(), substring-after(), string-length(), normalize-space(), translate() |
| numbers | number(), sum(), floor(), ceiling(), round() |
| booleans | boolean(), not(), true(), false(), lang() |
XPath in the Browser
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
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
Expressions people search for
| Need | XPath 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'] |
XPath 2.0 to 4.0
sequences, types, if and for, regex, maps and arrays
What 2.0 and 3.1 Added
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
| Spec | Status in October 2026 |
|---|---|
| XPath 3.1, XSLT 3.0, XQuery 3.1 | W3C Recommendations since 2017 |
| XPath 4.0, XSLT 4.0, XQuery 4.0 | drafts of the W3C QT4 Community Group |
XSLT
templates, the identity transform, sorting and grouping, and XSLT in browsers
A First Stylesheet
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
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
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 > 40">expensive</xsl:when>
<xsl:when test="price > 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
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
Being removed from Chrome
| Chrome version | Date | XSLT |
|---|---|---|
| 143 | December 2025 | deprecated, with console warnings |
| 158 | November 17, 2026 | stops working on Stable, except with an origin trial or enterprise policy |
| 176 | August 17, 2027 | removed for everyone |
Grouping in XSLT 1.0
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> </xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>"""))
print(str(xslt(etree.parse("books.xml"))).strip())
Parameters From Code
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 < $max])"/>
</xsl:template>
</xsl:stylesheet>"""))
doc = etree.parse("books.xml")
print(xslt(doc), xslt(doc, max="50"))
XQuery
FLWOR queries over XML documents and databases
FLWOR
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>
Parsing Models
DOM, SAX, pull parsers and streaming large files
Four Ways to Read XML
Pick by file size and access pattern
| Model | How it works | Memory | Use for |
|---|---|---|---|
| DOM / tree | the whole document becomes a tree | about 3 to 10 times the file size | documents up to tens of MB, random access, editing |
| SAX | callbacks for each start tag, text and end tag | constant | huge files, one pass, simple extraction |
| pull (StAX, XmlReader) | your code asks for the next event | constant | huge files with easier control flow than SAX |
| streaming tree (iterparse) | builds elements, you discard them after use | small | huge files when you still want element objects |
Stream a Huge File
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
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))
XML in Code
parsing, building and querying XML in six languages
Read a Value
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
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
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"))
XML Security
XXE, billion laughs, XPath injection and safe parser settings
XXE: External Entities
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
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
One line per language
| Language | Setting |
|---|---|
| Python | use defusedxml, or lxml with resolve_entities=False and no_network=True |
| Java | factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true) |
| .NET | XmlReaderSettings.DtdProcessing = DtdProcessing.Prohibit (the default for XmlReader) |
| PHP 8 | safe by default; never pass LIBXML_NOENT or LIBXML_DTDLOAD for untrusted input |
| Go | encoding/xml does not resolve external entities |
| browsers | DOMParser never loads external entities or DTDs |
XSLT and XPath Risks
Untrusted stylesheets are code
| Risk | Mitigation |
|---|---|
| document() and xsl:include read files and URLs | lxml: XSLTAccessControl.DENY_ALL; Saxon: restrict URI resolvers |
| extension functions run code | do not enable them for untrusted stylesheets |
| XPath built from input | use variables, as shown in the XPath section |
| huge or deep documents | cap input size; lxml huge_tree=False is the default |
XML and JSON
conversion conventions, pitfalls and when to choose which
Comparison
The same data, two formats
| XML | JSON | |
|---|---|---|
| attributes and elements | both, plus mixed content | only keys and values |
| schemas | XSD, RELAX NG, Schematron, mature | JSON Schema |
| comments | yes | no |
| namespaces | yes | no |
| data types | text unless a schema says otherwise | string, number, boolean, null, array, object |
| size | larger, end tags repeat names | smaller |
| typical use | documents, publishing, finance, government, config | web APIs and app data |
Conversion Conventions
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
Round trips are not lossless
| XML feature | Typical JSON result |
|---|---|
| attribute vs child element | both become keys; the difference is a naming convention |
| one vs many children | object vs array, depending on the count |
| order of different children | lost unless you use arrays everywhere |
| mixed content | text pieces merge or drop |
| comments and processing instructions | dropped |
| namespaces | kept as prefixed names or dropped |
XML and HTML
XHTML, why HTML is not XML, and parsing HTML with XML tools
Why HTML Is Not XML
The same markup, two parsers
from lxml import etree, html
page = "<p>One<br>Two<p>Three 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
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
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))
XML Formats You Meet
sitemaps, feeds, SVG, SOAP, Office files and build files
Sitemaps
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
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
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
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
Formats built on XML
| File | What is inside |
|---|---|
| .docx, .xlsx, .pptx | a ZIP of XML parts (Office Open XML, ISO/IEC 29500) |
| .odt, .ods | a ZIP of XML parts (OpenDocument) |
| .epub | a ZIP with XHTML and XML metadata |
| pom.xml | Maven build configuration |
| .csproj, .vbproj | MSBuild projects |
| AndroidManifest.xml, layout files | Android apps |
| .plist | macOS and iOS settings, XML or binary |
| .gpx, .kml | GPS tracks and map data |
| .xliff | translation files |
Build Files
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>
Tools & Command Line
xmllint, xmlstarlet, Saxon and editor support
xmllint
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
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
Formatting and validation while you type
| Editor | Extension |
|---|---|
| VS Code | XML by Red Hat (redhat.vscode-xml): format, validate against XSD and DTD, completion |
| Notepad++ | XML Tools plugin: pretty print, check syntax, validate, XPath |
| JetBrains IDEs | built in: XSD and DTD aware completion and validation |
| Oxygen XML Editor | commercial, the reference tool for XSLT, XQuery and schemas |
Canonical XML
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
Fail the build on any broken file
find . -name "*.xml" -print0 | xargs -0 -n1 xmllint --noout xmllint --noout --schema schema.xsd config/*.xml
Troubleshooting
the errors that cost the most time and how to fix them
Errors and Fixes
Messages from Java, Python, .NET and browsers
| Message | Usual cause |
|---|---|
| Content is not allowed in prolog | text, a BOM in the wrong encoding, or HTML before the first tag |
| XML declaration allowed only at the start of the document | blank line or space before <?xml |
| Invalid byte 1 of 1-byte UTF-8 sequence | the file is Latin-1 or Windows-1252 but declared or assumed UTF-8 |
| Entity 'nbsp' not defined | an HTML entity; use   |
| The prefix "x" for element "x:y" is not bound | missing xmlns:x declaration |
| junk after document element | two root elements, or text after the root |
| XPath returns nothing | a 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
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
Versions & Specs
XML, namespaces, schemas, XPath, XSLT and XQuery versions
The Recommendations
What is current in October 2026
| Spec | Current version | Since |
|---|---|---|
| XML | 1.0 Fifth Edition | November 2008 |
| XML 1.1 | Second Edition, rarely used | 2006 |
| Namespaces in XML | 1.0 Third Edition | December 2009 |
| XML Schema (XSD) | 1.1, with 1.0 still the most implemented | April 2012 |
| XPath | 3.1 | March 2017 |
| XSLT | 3.0 | June 2017 |
| XQuery | 3.1 | March 2017 |
| RELAX NG | ISO/IEC 19757-2 | 2003, updated 2008 |
Where It Came From
A short history
| Year | Event |
|---|---|
| 1986 | SGML becomes ISO 8879; XML is a simplified subset of it |
| 1998 | XML 1.0 becomes a W3C Recommendation in February |
| 1999 | Namespaces, XPath 1.0 and XSLT 1.0 |
| 2001 | XML Schema 1.0 |
| 2006 | JSON is described in RFC 4627 and starts replacing XML in web APIs |
| 2007 | XPath 2.0, XSLT 2.0 and XQuery 1.0 |
| 2017 | XPath 3.1, XSLT 3.0 and XQuery 3.1 |
| 2026 | Chrome 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.
- Basics: the toolbox and a first document, syntax rules, names and characters, escaping and CDATA, and well formed versus valid.
- Structure: namespaces, encodings, and DTD, XSD, RELAX NG and Schematron.
- Querying: XPath 1.0, XPath 2.0 to 4.0, XSLT, and XQuery.
- Programming: parsing models, XML in six languages, XXE and billion laughs, and XML and JSON.
- In practice: sitemaps, feeds, SVG, SOAP and Office files, command line tools, troubleshooting, and versions and history.
XML Quick Reference
The forms people look up most often, one line each.
| Task | XML |
|---|---|
| Declaration | <?xml version="1.0" encoding="UTF-8"?> |
| Element | <title>Dune</title> |
| Empty element | <br/> |
| Attribute | <book id="b1"> |
| Comment | <!-- note --> |
| Escape < and & | < and & |
| Raw text | <![CDATA[ a < b ]]> |
| Any character | © or © |
| Default namespace | xmlns="https://example.com/ns" |
| Prefixed namespace | xmlns:dc="http://purl.org/dc/elements/1.1/" |
| Keep whitespace | xml:space="preserve" |
| Every element named book | //book |
| An attribute | //book/@id |
| Filter | //book[price > 30] |
| Text contains | //book[contains(title, 'XML')] |
| Count | count(//book) |
| Validate against XSD | xmllint --noout --schema s.xsd f.xml |
| Pretty print | xmllint --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.
- The parser detects the encoding from the byte order mark or the XML declaration and decodes the bytes into characters.
- It reads the optional DOCTYPE and, depending on settings, expands entities; safe settings refuse external entities.
- It checks well-formedness while reading tags, attributes and text, and stops with a fatal error at the first violation.
- It resolves namespace prefixes, so every element and attribute gets an expanded name of namespace URI plus local name.
- 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.
- 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 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 < and & as & in text and attribute values; write " as " inside attribute values in double quotes. > can be written as >, and ' as '. Any other character can appear directly in a UTF-8 document or as a numeric character reference such as ©.
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.