Getting Started
XML Document Structure
XML documents start with an XML declaration. Elements must be properly nested and closed. Attributes provide additional metadata. xmlns declares XML namespaces to avoid naming conflicts.
<?xml version="1.0" encoding="UTF-8"?>
<!-- This is a comment -->
<bookstore xmlns="https://example.com/books">
<book id="b1" category="fiction">
<title lang="en">The Great Gatsby</title>
<author>F. Scott Fitzgerald</author>
<price currency="USD">12.99</price>
</book>
<book id="b2" category="programming">
<title lang="en">XML Guide</title>
<author>John Doe</author>
<price currency="USD">29.99</price>
</book>
</bookstore>XML Declaration
The XML declaration is the prolog's first line. 'version' is required (1.0 or 1.1). 'encoding' defaults to UTF-8. 'standalone' tells parsers whether external DTD declarations are needed.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- version is required; encoding and standalone are optional -->
<!-- encoding defaults to UTF-8 -->
<!-- standalone="yes" means no external markup declarations -->
<root>
<child>text</child>
</root>Comments & Processing Instructions
Comments use <!-- --> and cannot nest or contain '--' inside. Processing Instructions (PIs) like xml-stylesheet pass app-specific info to the parser. PIs use <?target data?>.
<?xml version="1.0"?>
<!-- a comment: ignored by the parser, cannot contain -- -->
<?xml-stylesheet type="text/xsl" href="style.xsl"?>
<root>
<!-- comments can span
multiple lines -->
<item>data</item>
</root>Well-formed XML
Well-formed means: one root element, proper nesting, all tags closed, attributes quoted, no duplicate attributes. A document MUST be well-formed to be parseable; validity (against a DTD/Schema) is optional.
<!-- WELL-FORMED: single root, properly nested, closed -->
<root>
<a>
<b>text</b>
</a>
</root>
<!-- NOT well-formed: two roots, overlapping tags -->
<!-- <a><b></a></b> -->
<!-- <x></x><y></y> (no single root) -->
<!-- All attributes must be quoted -->
<item id='1' type="x"/>XML Prolog & Whitespace
The prolog includes the XML declaration and optional DOCTYPE. xml:space='preserve' tells parsers to keep whitespace; 'default' allows normal handling. Whitespace between elements is generally insignificant.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root SYSTEM "root.dtd">
<root xml:space="preserve">
<item> spaced text </item>
<item xml:space="default"> trimmed </item>
</root>CDATA Overview
CDATA sections mark text that the parser should not interpret as markup. Inside CDATA, characters like <, >, and & need no escaping. CDATA cannot be nested and cannot contain the literal ']]>'.
<?xml version="1.0"?>
<root>
<code><![CDATA[
if (a < b && c > d) {
console.log("no escaping needed: < > & ");
}
]]></code>
</root>Elements & Attributes
Elements vs Attributes
Attributes suit metadata (ids, types, flags); elements suit data with structure or repeated values. An attribute appears once per element and cannot hold nested structure. There is no strict rule—consistency matters most.
<!-- Attributes: metadata/identifiers; Elements: data -->
<person id="p1">
<name>Alice</name>
<age>30</age>
</person>
<!-- vs. putting everything in attributes -->
<person id="p1" name="Alice" age="30"/>
<!-- Rule of thumb: use elements for data you display,
attributes for IDs, types, and metadata -->Empty Elements
An empty element has no content. The self-closing form <tag/> is shorthand for <tag></tag>. The space before '/>' is a style convention for readability and XHTML compatibility.
<!-- Three equivalent empty-element forms -->
<br></br>
<br/>
<br />
<!-- empty element with attributes -->
<img src="logo.png" alt="Logo" width="200" height="100"/>
<!-- self-closing with namespace -->
<xlink:link xmlns:xlink="http://www.w3.org/1999/xlink" href="#sec1"/>Nested Elements
Elements nest hierarchically and must close in reverse order—they cannot overlap. Nesting expresses structure. Deeply nested trees are valid but can hurt readability and parsing performance.
<library>
<section name="fiction">
<book>
<title>Book A</title>
<authors>
<author>Author 1</author>
<author>Author 2</author>
</authors>
</book>
</section>
</library>
<!-- WRONG: tags must not overlap -->
<!-- <a><b></a></b> -->Attribute Values & Quotes
Attribute values must be quoted with single or double quotes. Double quotes inside double-quoted values must be escaped as ". An attribute may appear only once per element—use child elements for repeated values.
<!-- Both single and double quotes are allowed -->
<item id="q1" name='quick'/>
<msg text="He said "hi""/>
<msg text='She said 'hi''/>
<!-- attribute values MUST be quoted -->
<!-- <item id=q1/> is NOT well-formed -->
<!-- an attribute can appear at most once per element -->
<point x="1" y="2" z="3"/>Default & Fixed Attributes
DTD attribute declarations support #REQUIRED (must appear), #IMPLIED (optional), #FIXED (constant value), and a literal default. A #FIXED attribute must always equal its declared value or the document is invalid.
<!-- In a DTD, attributes can have defaults -->
<!ATTLIST item
type CDATA "standard"
status CDATA #REQUIRED
version CDATA #FIXED "1.0"
note CDATA #IMPLIED>
<item status="active"/>
<!-- 'type' defaults to "standard", 'version' is fixed,
'note' is optional (#IMPLIED) -->Element Naming Rules
Element names must start with a letter or underscore and may contain letters, digits, hyphens, underscores, and dots. Names cannot start with 'xml' (any case). XML is case-sensitive, so <Tag> and <tag> differ.
<!-- Names: start with a letter or underscore,
continue with letters, digits, hyphens, underscores, dots -->
<my-element>ok</my-element>
<my.element>ok</my.element>
<my_element>ok</my_element>
<_private>ok</_private>
<x-1>ok</x-1>
<!-- NOT allowed: starting with a digit or 'xml', containing spaces -->
<!-- <2way/> -->
<!-- <my element/> -->
<!-- <XmlTag/> (names are case-sensitive; 'xml' prefix is reserved) -->Namespaces
Default Namespace
A default namespace (xmlns=URI) applies to the element where it is declared and all unprefixed descendants. Child elements inherit it unless overridden. Attributes never use the default namespace.
<?xml version="1.0"?>
<table xmlns="http://www.w3.org/1999/xhtml">
<tr>
<td>Cell content</td>
<td>More content</td>
</tr>
</table>
<!-- All elements without a prefix use the default namespace -->Namespace Prefix
A namespace prefix is declared with xmlns:prefix=URI and used as prefix:element. The prefix is just an alias—only the URI matters for identity. Prefixes are case-sensitive.
<?xml version="1.0"?>
<root xmlns:bk="https://example.com/books"
xmlns:au="https://example.com/authors">
<bk:book bk:id="b1">
<au:author au:name="Jane Doe"/>
<bk:title>XML Guide</bk:title>
</bk:book>
</root>Multiple Namespaces
A single document can mix many namespaces. Declare each prefix once (usually on the root) and reuse it. Declaring a namespace on an element applies to that element and its descendants.
<?xml version="1.0"?>
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<m:GetPrice xsi:type="xsd:string"
xmlns:m="https://example.com/pricing">
<m:Item>Apples</m:Item>
</m:GetPrice>
</soap:Body>
</soap:Envelope>Namespace Scope
Namespace declarations are scoped to the element they appear on and its descendants. A redeclaration inside a child overrides the parent's prefix mapping for that subtree. The original mapping is restored after the child closes.
<?xml version="1.0"?>
<root xmlns:a="http://a.example">
<a:item>A</a:item>
<child xmlns:a="http://b.example">
<!-- here 'a' is REBOUND to the b.example URI -->
<a:item>B</a:item>
</child>
<!-- outside child, 'a' is back to a.example -->
<a:item>A again</a:item>
</root>Common Namespaces
These URIs are well-known identifiers, not URLs that must be fetched. The xsi namespace provides schema-instance attributes (type, nil, schemaLocation). XSD, XSL, SVG, SOAP, Atom, and Dublin Core (dc) each have standard URIs.
<!-- Frequently used namespace URIs -->
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd = "http://www.w3.org/2001/XMLSchema"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
xmlns:svg = "http://www.w3.org/2000/svg"
xmlns:html = "http://www.w3.org/1999/xhtml"
xmlns:soap = "http://schemas.xmlsoap.org/soap/envelope/"
xmlns:atom = "http://www.w3.org/2005/Atom"
xmlns:dc = "http://purl.org/dc/elements/1.1/"Target Namespace in XSD
A schema's targetNamespace is the namespace of the elements it defines. elementFormDefault='qualified' means locally declared elements are in the target namespace. The tns prefix is a convention for 'this namespace'.
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="https://example.com/orders"
xmlns:tns="https://example.com/orders"
elementFormDefault="qualified">
<xs:element name="order" type="tns:OrderType"/>
<xs:complexType name="OrderType">
<xs:sequence>
<xs:element name="item" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>CDATA & Entities
Predefined Entities
XML predefines five entities: < (<), > (>), & (&), ' ('), " ("). & must ALWAYS be escaped (even in CDATA-free text); < must be escaped except as a tag start; quotes matter mainly in attributes.
<?xml version="1.0"?>
<root>
<lt>less than: <</lt>
<gt>greater than: ></gt>
<amp>ampersand: &</amp>
<apos>apostrophe: '</apos>
<quot>quote: "</quot>
<!-- < > & ' " are the 5 built-in entities -->
</root>Character References
Character references use a Unicode code point: © (decimal) or © (hex) for ©. They work in element text and attribute values and are not affected by encoding. They can represent any Unicode character.
<?xml version="1.0" encoding="UTF-8"?>
<root>
<decimal>© 2024 Company</decimal>
<hex>© 2024 Company</hex>
<euro>Price: €10</euro>
<emoji>Smile: 😀</emoji>
</root>
<!-- &#NNN; decimal code point -->
<!-- &#xHHH; hexadecimal code point -->CDATA Sections
CDATA sections let you include <, >, and & without escaping—useful for code and markup. CDATA cannot contain ']]>' literally; to embed it, split the section as shown. CDATA is character data, not parsed as elements.
<?xml version="1.0"?>
<root>
<script><![CDATA[
function check(a, b) {
if (a < b && b > 0) {
return a & b;
}
}
]]></script>
<!-- to include ]]> inside CDATA, split it -->
<data><![CDATA[foo]]]]><![CDATA[>bar]]></data>
</root>Custom Entities (DTD)
Internal entities are declared in a DTD and expanded wherever referenced. They act like text macros and can reference other entities. Useful for repeated boilerplate. Beware: external/general entities can be an XXE security risk.
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY company "Acme Corp.">
<!ENTITY version "2.0">
<!ENTITY copyright "Copyright © 2024 &company;">
]>
<root>
<product>&company; Toolkit v&version;</product>
<footer>©right;</footer>
</root>
<!-- Entities are expanded during parsing -->Escaping in Attributes
Inside attribute values you must escape & as &, and escape the quote character used to delimit the value (" or '). < should also be escaped. Newlines and tabs in attributes are normalized to spaces by the parser.
<?xml version="1.0"?>
<root>
<link url="https://example.com?a=1&b=2"/>
<msg text="Say "hello" & smile"/>
<path value='C:\Users\name'/>
<data value="<tagged>"/>
</root>Parameter Entities
Parameter entities (declared with %) are used only inside DTDs to build reusable content models. They are referenced as %name; (with a semicolon). General entities (&name;) are used in document content.
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY % inline " (#PCDATA | em | strong)* ">
<!ELEMENT p %inline;>
<!ELEMENT em %inline;>
<!ELEMENT strong %inline;>
<!ELEMENT root (p)*>
]>
<root>
<p>Hello <em>world</em> and <strong>XML</strong>.</p>
</root>DTD (Document Type Definition)
Internal DTD
An internal DTD is declared inline in the DOCTYPE within square brackets. It defines the structure: which elements exist, their content models, and attributes. Internal DTDs are self-contained but only apply to that one document.
<?xml version="1.0"?>
<!DOCTYPE note [
<!ELEMENT note (to, from, heading, body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>
<note>
<to>Alice</to>
<from>Bob</from>
<heading>Reminder</heading>
<body>Don't forget the meeting</body>
</note>External DTD
An external DTD lives in a separate .dtd file referenced by SYSTEM (private) or PUBLIC (public identifier + URI). External DTDs let many documents share one definition. SYSTEM 'file.dtd' is the most common form.
<!-- File: note.dtd -->
<!ELEMENT note (to, from, body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT body (#PCDATA)>
<!-- File: note.xml -->
<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
<to>Alice</to>
<from>Bob</from>
<body>Hello</body>
</note>Element Declarations
Content models use , (sequence), | (choice), ? (0-1), * (0+), + (1+). #PCDATA is text; mixed content allows text interleaved with elements. EMPTY means no content; ANY disables checking (avoid in production).
<!ELEMENT root (child1, child2*)>
<!-- content model operators -->
<!ELEMENT list (item+)> <!-- one or more item -->
<!ELEMENT opt (item?)> <!-- zero or one item -->
<!ELEMENT any (item1 | item2)*> <!-- choice, any number -->
<!ELEMENT text (#PCDATA)> <!-- parsed character data -->
<!ELEMENT mixed (#PCDATA | em)*> <!-- mixed content -->
<!ELEMENT empty EMPTY> <!-- no content -->
<!ELEMENT any ANY> <!-- any content -->Attribute Declarations
ATTLIST declares an attribute's type and default. ID is a unique identifier; IDREF/IDREFS reference IDs (for cross-links). NMTOKEN is a name token. Enumerated types list allowed values. Defaults: #REQUIRED, #IMPLIED, #FIXED, or a literal.
<!ATTLIST book
id ID #REQUIRED
category CDATA #IMPLIED
lang NMTOKEN "en"
status (draft|final) "draft"
version CDATA #FIXED "1.0">
<!-- Types: CDATA, ID, IDREF, IDREFS, NMTOKEN, NMTOKENS,
NOTATION, enumerated (a|b|c) -->
<!ATTLIST link ref IDREF #REQUIRED>Entity Declarations
General entities (&name;) expand in content; parameter entities (%name;) expand in the DTD. External parsed entities include other XML; unparsed entities (with NDATA) point to non-XML data referenced via NOTATION. External entities can cause XXE injection.
<!DOCTYPE root [
<!-- internal general entity -->
<!ENTITY name "Alice">
<!-- external parsed entity -->
<!ENTITY chap1 SYSTEM "chapter1.xml">
<!-- unparsed entity (binary, e.g. image) -->
<!ENTITY logo SYSTEM "logo.png" NDATA image>
<!-- notation declaration for unparsed entities -->
<!NOTATION image SYSTEM "image/png">
<!-- parameter entity (DTD-only) -->
<!ENTITY % block "(p | list)+">
]>
<root>&name;</root>DTD Validation
Validation checks that a document matches its DTD (element order, allowed attributes, ID uniqueness). Standard DOM parsers like minidom do NOT validate by default—use lxml (Python), xmllint (command line), or a validating parser.
<?xml version="1.0"?>
<!DOCTYPE root SYSTEM "schema.dtd">
<root>
<item id="x1">valid</item>
</root>
<!-- Validate with Python: -->
<!-- import xml.dom.minidom as m
dom = m.parse("file.xml")
# minidom does NOT validate; use lxml: -->
<!-- from lxml import etree
dtd = etree.DTD("schema.dtd")
tree = etree.parse("file.xml")
print(dtd.validate(tree)) -->XML Schema (XSD)
Schema Structure
An XSD is itself an XML document. The root <schema> declares the XSD namespace and a target namespace for the elements it defines. elementFormDefault='qualified' puts local elements in the target namespace.
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="https://example.com/order"
xmlns:tns="https://example.com/order"
elementFormDefault="qualified">
<xs:element name="order" type="tns:OrderType"/>
<xs:complexType name="OrderType">
<xs:sequence>
<xs:element name="customer" type="xs:string"/>
<xs:element name="total" type="xs:decimal"/>
</xs:sequence>
<xs:attribute name="id" type="xs:ID" use="required"/>
</xs:complexType>
</xs:schema>Simple Types
A simpleType restricts a base type with facets (pattern, minInclusive, enumeration, length, etc.). Simple types have only text content and attributes—no child elements. They are reusable across the schema.
<xs:simpleType name="emailType">
<xs:restriction base="xs:string">
<xs:pattern value="[^@]+@[^@]+.[^@]+"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ageType">
<xs:restriction base="xs:integer">
<xs:minInclusive value="0"/>
<xs:maxInclusive value="120"/>
</xs:restriction>
</xs:simpleType>
<xs:element name="email" type="tns:emailType"/>
<xs:element name="age" type="tns:ageType"/>Complex Types
A complexType can hold child elements and attributes. sequence enforces order; choice allows one of several; all allows any order (each at most once). Compositors can nest to express rich structures.
<xs:complexType name="AddressType">
<xs:sequence>
<xs:element name="street" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="zip" type="xs:string"/>
</xs:sequence>
<xs:attribute name="country" type="xs:string" default="US"/>
</xs:complexType>
<!-- Content model indicators: sequence, choice, all -->
<xs:complexType name="ContactType">
<xs:choice>
<xs:element name="phone" type="xs:string"/>
<xs:element name="email" type="xs:string"/>
</xs:choice>
</xs:complexType>Built-in Types
XSD provides a rich type hierarchy: strings, numbers (integer, decimal, float), dates/times, boolean, anyURI, ID/IDREF, and QName. Each supports facets like length, pattern, and enumeration for further restriction.
<!-- Common XSD built-in types -->
<xs:element name="count" type="xs:integer"/>
<xs:element name="price" type="xs:decimal"/>
<xs:element name="flag" type="xs:boolean"/>
<xs:element name="name" type="xs:string"/>
<xs:element name="date" type="xs:date"/> <!-- 2024-01-15 -->
<xs:element name="time" type="xs:time"/> <!-- 13:45:00 -->
<xs:element name="dt" type="xs:dateTime"/> <!-- 2024-01-15T13:45:00 -->
<xs:element name="id" type="xs:ID"/>
<xs:element name="ref" type="xs:IDREF"/>
<xs:element name="uri" type="xs:anyURI"/>Restrictions (Facets)
Facets constrain simple types: enumeration (allowed values), pattern (regex), length/minLength/maxLength, min/maxInclusive/Exclusive, totalDigits, fractionDigits, and whiteSpace (preserve/replace/collapse).
<xs:simpleType name="ColorType">
<xs:restriction base="xs:string">
<xs:enumeration value="red"/>
<xs:enumeration value="green"/>
<xs:enumeration value="blue"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="PasswordType">
<xs:restriction base="xs:string">
<xs:minLength value="8"/>
<xs:maxLength value="32"/>
<xs:pattern value="[A-Za-z0-9!@#]+"/>
</xs:restriction>
</xs:simpleType>Elements & Attributes in XSD
minOccurs/maxOccurs control cardinality (default 1); maxOccurs='unbounded' allows any number. Attribute 'use' is required/optional/prohibited, with an optional default or fixed value. Anonymous complex types inline the definition.
<xs:element name="product">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"
minOccurs="1" maxOccurs="1"/>
<xs:element name="tag" type="xs:string"
minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="sku" type="xs:string" use="required"/>
<xs:attribute name="weight" type="xs:decimal" use="optional"
default="0.0"/>
</xs:complexType>
</xs:element>XPath
Path Expressions
XPath uses path-like expressions. '/' selects from the root; '//' selects descendants at any depth. '*' is a wildcard for any element; '@' selects attributes. An expression starting with '/' is absolute.
<!-- Sample: <bookstore><book><title>..</title></book></bookstore> -->
/bookstore/book/title <!-- absolute path -->
bookstore/book/title <!-- relative path -->
//title <!-- any title, anywhere -->
/bookstore//title <!-- title anywhere under bookstore -->
bookstore/* <!-- all children of bookstore -->
//@lang <!-- all lang attributes -->
//@* <!-- all attributes -->Predicates
Predicates in [ ] filter a node set. Indexing is 1-based. last() and position() refer to the node's position in its context. Predicates can test element text, attributes (@name), or computed values.
//book[1] <!-- first book -->
//book[last()] <!-- last book -->
//book[position() <= 3] <!-- first three books -->
//book[price > 25] <!-- books with price > 25 -->
//book[@category='web'] <!-- books in the web category -->
//book[author='Smith'] <!-- books authored by Smith -->
//book[@lang][2] <!-- 2nd book that has a lang attr -->Axes
Axes define navigation directions: child (default), descendant, parent (..), ancestor, following-sibling, preceding-sibling, attribute (@), self (.), descendant-or-self (//). Most have shorthand forms used in practice.
child::book <!-- default axis: children named book -->
descendant::title <!-- all title descendants -->
parent::* <!-- the parent element (shorthand: ..) -->
ancestor::section <!-- all section ancestors -->
following-sibling::p <!-- p elements after this one -->
preceding::item <!-- items before this in document order -->
attribute::id <!-- the id attribute (shorthand: @id) -->
self::node() <!-- the current node (shorthand: .) -->Functions
XPath 1.0 has built-in functions: count, sum, string-length, contains, starts-with, normalize-space, name, concat, substring, round, and more. XPath 2.0+ vastly expands the function library and adds typing.
count(//book) <!-- number of book elements -->
string-length(//title) <!-- length of first title's text -->
contains(//name, 'Ali') <!-- true if name contains 'Ali' -->
starts-with(@id, 'b') <!-- true if id starts with 'b' -->
normalize-space(//summary) <!-- collapse whitespace -->
name(//*[1]) <!-- name of the first element -->
concat(/a, /b) <!-- concatenate strings -->
sum(//price) <!-- sum of all price values -->Operators
XPath uses = for equality (single =, not ==), and the operators 'and'/'or' for logic (not && / ||). 'div' is division and 'mod' is modulo, since '/' is reserved for paths. '|' computes the union of node sets.
<!-- Comparison: =, !=, <, >, <=, >= -->
//book[price = 29.99]
//book[price < 20]
<!-- Logical: and, or -->
//book[price > 10 and price < 30]
//book[@cat='a' or @cat='b']
<!-- Arithmetic: + - * div mod -->
//item[price * quantity > 100]
5 div 2 <!-- 2.5 -->
5 mod 2 <!-- 1 -->
<!-- Union: | -->
//book | //magazineXPath Examples
These patterns combine paths, predicates, and functions for real queries. text() selects the text node; min() requires XPath 2.0+. The //* wildcard with a predicate is a common way to find elements by attribute across the whole tree.
<!-- Select all book titles priced over 20 -->
//book[price > 20]/title/text()
<!-- The category of the cheapest book -->
//book[price = min(//book/price)]/@category
<!-- Authors of books in the 'programming' category -->
//book[@category='programming']/author
<!-- Every 2nd item in each list -->
//list/item[position() mod 2 = 0]
<!-- Elements with a specific attribute value -->
//*[@lang='fr']XSLT Transformations
Basic Transformation
An XSLT stylesheet is XML. Templates match nodes with XPath; apply-templates recursively processes selected nodes. value-of extracts text. The match='/' template runs first, on the document root.
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>Book List</h2>
<xsl:apply-templates select="bookstore/book"/>
</body>
</html>
</xsl:template>
<xsl:template match="book">
<p><xsl:value-of select="title"/> - <xsl:value-of select="price"/></p>
</xsl:template>
</xsl:stylesheet>xsl:template & apply-templates
match templates fire when the processor visits matching nodes; named templates are called explicitly with call-template. apply-templates without a select processes all children, enabling recursive, rule-driven transformations.
<xsl:template match="book">
<div class="book">
<xsl:apply-templates select="title"/>
<xsl:apply-templates select="author"/>
</div>
</xsl:template>
<xsl:template match="title">
<h3><xsl:apply-templates/></h3>
</xsl:template>
<!-- named template (like a function) -->
<xsl:template name="divider">
<hr/>
</xsl:template>xsl:value-of & xsl:for-each
xsl:for-each iterates a node set, changing the context node. It is often simpler than templates for flat output, but overuse makes stylesheets procedural. Prefer templates and apply-templates for recursive structures.
<xsl:template match="/">
<ul>
<xsl:for-each select="bookstore/book">
<li>
<xsl:value-of select="title"/>
(<xsl:value-of select="@category"/>)
</li>
</xsl:for-each>
</ul>
</xsl:template>
<!-- value-of outputs the string value of an expression -->xsl:if & xsl:choose
xsl:if has no else—use xsl:choose for multi-way branches. The 'test' expression follows XPath boolean rules: a non-empty node-set is true, an empty one is false, non-zero numbers are true.
<xsl:for-each select="bookstore/book">
<xsl:if test="price > 25">
<premium><xsl:value-of select="title"/></premium>
</xsl:if>
<xsl:choose>
<xsl:when test="@category='fiction'">F</xsl:when>
<xsl:when test="@category='programming'">P</xsl:when>
<xsl:otherwise>?</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<!-- test uses XPath; non-empty node-sets and non-zero numbers are true -->xsl:sort & xsl:variable
xsl:sort reorders the current node set by one or more keys; data-type is 'text' or 'number'. Variables (xsl:variable) are immutable and scoped. Remember to escape < as < inside attribute values in the stylesheet XML.
<xsl:variable name="max-price" select="50"/>
<xsl:for-each select="bookstore/book[price < $max-price]">
<xsl:sort select="price" data-type="number" order="descending"/>
<xsl:sort select="title" order="ascending"/>
<p>
<xsl:value-of select="title"/>: <xsl:value-of select="price"/>
</p>
</xsl:for-each>
<!-- Note: in XSLT, < must be written < inside test attributes -->xsl:apply-templates & modes
Modes let one set of source nodes be processed in different ways (e.g., a table of contents vs. full entries). The mode attribute on both template and apply-templates must match. Without a mode, only no-mode templates apply.
<!-- A template can have a 'mode' for different outputs -->
<xsl:template match="book" mode="list">
<li><xsl:value-of select="title"/></li>
</xsl:template>
<xsl:template match="book" mode="detail">
<div><h3><xsl:value-of select="title"/></h3>
<p><xsl:value-of select="author"/></p></div>
</xsl:template>
<!-- Invoke a specific mode -->
<xsl:apply-templates select="bookstore/book" mode="list"/>
<xsl:apply-templates select="bookstore/book" mode="detail"/>XQuery
FLWOR Expressions
FLWOR is XQuery's core construct: for binds variables to sequence items, let binds computed values, where filters, order by sorts, and return shapes the output. It resembles SQL's SELECT-FROM-WHERE.
for $b in /bookstore/book
let $discount := $b/price * 0.9
where $b/price > 20
order by $b/price descending
return
<book>
<title>{ $b/title/text() }</title>
<salePrice>{ $discount }</salePrice>
</book>
<!-- FLWOR = For, Let, Where, Order by, Return -->Path Expressions
XQuery embeds XPath directly. doc('file.xml') loads a document; collection('uri') queries multiple documents. Path expressions return sequences of nodes, which FLWOR expressions can iterate over.
(: XQuery uses XPath for navigation :)
/bookstore/book/title/text()
/bookstore/book[@category='web']
//author[contains(., 'Smith')]
(: Use doc() to open an external document :)
doc('catalog.xml')//book[price < 30]
(: collection() queries a set of documents :)
collection('/db/books')//titlePredicates & Joins
Multiple 'for' clauses form a join (like SQL JOIN). XQuery performs joins by iterating combinations and filtering with where. For large datasets, explicit joins via predicates or keys are more efficient than nested loops.
(: Join books to authors by id :)
for $b in doc('books.xml')//book
for $a in doc('authors.xml')//author
where $b/@author-id = $a/@id
return
<entry>
<title>{ $b/title/text() }</title>
<author>{ $a/name/text() }</author>
</entry>
(: Equivalent join with a predicate :)
for $b in doc('books.xml')//book
return
<entry>{ $b/title, $b/@author-id }</entry>Conditional & Quantified
XQuery has if-then-else (the else is mandatory), quantified expressions (some/every ... satisfies), and typeswitch for branching on node type. Conditions use XPath-style boolean semantics.
(: if-then-else :)
for $b in //book
return
if ($b/price > 30) then <expensive>{ $b/title }</expensive>
else <cheap>{ $b/title }</cheap>
(: quantified expressions :)
some $a in //author satisfies contains($a, 'Smith')
every $p in //price satisfies $p > 0
(: typeswitch for type-based dispatch :)
typeswitch($node)
case element(book) return "book"
case element(author) return "author"
default return "other"Functions
XQuery reuses XPath functions and adds many (string-join, distinct-values, avg, min, max). User functions are declared in the module with parameter and return types, in the local: namespace by convention.
(: Built-in functions :)
concat('a', 'b', 'c')
string-join(//title, ', ')
count(//book)
distinct-values(//book/@category)
avg(//book/price)
max(//book/price)
(: User-defined function :)
declare function local:discount($price as xs:decimal)
as xs:decimal {
$price * 0.9
};
local:discount(29.99)Constructing XML
XQuery builds XML with direct constructors (literal tags with { } for embedded expressions) or computed constructors (element name { ... }). Curly braces evaluate enclosed expressions; attributes can embed values inline.
(: Direct element construction :)
<result>
<count>{ count(//book) }</count>
<books>
{ for $b in //book return <book title="{ $b/title/text() }"/> }
</books>
</result>
(: Computed constructors :)
element result {
attribute total { count(//book) },
for $b in //book
return element book { $b/title/text() }
}
(: Enclosed expressions use { } to embed values -->DOM Parsing
Loading XML (JavaScript)
DOMParser turns an XML string into a DOM tree in the browser; parse errors appear as a <parsererror> element rather than an exception. Node.js has no built-in XML DOM—use xmldom, @xmldom/xmldom, or a streaming parser.
// Browser: parse an XML string
const parser = new DOMParser();
const doc = parser.parseFromString(xmlString, "application/xml");
const errorNode = doc.querySelector("parsererror");
if (errorNode) {
console.error("Parse error:", errorNode.textContent);
}
const books = doc.getElementsByTagName("book");
for (const book of books) {
console.log(book.getAttribute("id"));
}
// Node.js: use xmldom or fast-xml-parser insteadDOM Traversal
The DOM API exposes documentElement, children, childNodes, and nextSibling for traversal. nodeType 1 is an element, 3 is text, 8 is a comment. document.evaluate runs XPath in browsers (Node needs xpath libraries).
// JavaScript DOM API (also works for XML documents)
const root = doc.documentElement; // <bookstore>
const firstBook = root.children[0]; // first <book>
const titles = doc.getElementsByTagName("title");
// walk children
let node = root.firstChild;
while (node) {
if (node.nodeType === 1) { // Element node
console.log(node.tagName);
}
node = node.nextSibling;
}
// XPath against the DOM
const it = doc.evaluate("//book[@id='b1']", doc, null,
XPathResult.ANY_TYPE, null);
const found = it.iterateNext();Modifying DOM
createElement/setAttribute/appendChild build and attach nodes; textContent sets text. XMLSerializer turns a DOM back into a string. The DOM is live—mutations reflect immediately in any references.
const book = doc.createElement("book");
book.setAttribute("id", "b3");
const title = doc.createElement("title");
title.textContent = "New Book";
book.appendChild(title);
root.appendChild(book); // add to tree
// change an existing element
const price = doc.querySelector("book:nth-child(2) price");
price.textContent = "19.99";
price.setAttribute("currency", "EUR");
// serialize back to a string
const xml = new XMLSerializer().serializeToString(doc);Creating Elements
minidom lets you build a document from scratch: createDocument for the root, createElement/setAttribute/createTextNode for content, appendChild to link. toprettyxml serializes with indentation. minidom is simple but slow for large trees.
// Python: xml.dom.minidom
from xml.dom import minidom
impl = minidom.getDOMImplementation()
doc = impl.createDocument(None, "root", None)
root = doc.documentElement
item = doc.createElement("item")
item.setAttribute("id", "1")
item.appendChild(doc.createTextNode("Hello"))
root.appendChild(item)
print(doc.toprettyxml(encoding="UTF-8").decode())Java DOM Parsing
Java's JAXP DOM loads the whole document into memory via DocumentBuilder. setNamespaceAware(true) keeps namespace prefixes. The DOM is convenient for random access but memory-heavy for large files—prefer StAX/SAX then.
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.*;
DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
f.setNamespaceAware(true);
Document doc = f.newDocumentBuilder().parse(new File("data.xml"));
Element root = doc.getDocumentElement();
NodeList books = root.getElementsByTagName("book");
for (int i = 0; i < books.getLength(); i++) {
Element b = (Element) books.item(i);
String id = b.getAttribute("id");
String title = b.getElementsByTagName("title")
.item(0).getTextContent();
System.out.println(id + ": " + title);
}lxml (Python)
lxml is a fast, feature-rich Python binding for libxml2. It supports full XPath, XSLT, validation (DTD/XSD/RelaxNG), and incremental parsing. Its ElementPath (find/findall) is a subset of XPath; use xpath() for full XPath.
from lxml import etree
# parse and validate against a schema
tree = etree.parse("data.xml")
root = tree.getroot()
# XPath with namespaces
ns = {"bk": "https://example.com/books"}
for book in root.xpath("//bk:book", namespaces=ns):
print(book.get("id"), book.find("bk:title", namespaces=ns).text)
# build an element tree
root = etree.Element("root")
child = etree.SubElement(root, "item", id="1")
child.text = "Hello"
print(etree.tostring(root, pretty_print=True, encoding="unicode"))SAX Parsing
SAX Handler (Python)
SAX is event-driven and streaming: the parser calls startElement, characters, and endElement as it reads, never building a full tree. It is memory-efficient for huge files. You must track state (e.g., current element) yourself.