Querying, Transformation & Real-World XML

XSLT: Transforming XML

XSLT transforms XML documents into other formats — HTML pages, different XML structures, plain text, or CSV. It is a functional, template-based language that applies rules to an XML input and produces an output document.

What XSLT Does

XSLT (eXtensible Stylesheet Language Transformations) takes an XML input document and a stylesheet (itself an XML document with transformation rules), and produces an output. The input is always XML; the output can be XML, HTML, or plain text.

The core use cases:

  • Transform data XML into HTML reports for display
  • Convert one XML schema to another for system integration
  • Generate documentation from XML source
  • Transform RSS feeds into styled web pages
  • Produce multiple output formats from a single XML source

The XSLT Processing Model

  1. The XSLT processor reads the input XML and builds a tree
  2. It reads the stylesheet and compiles the template rules
  3. Starting from the document root, it matches nodes against templates
  4. When a template matches, the processor executes it — outputting literal markup and using XSLT instructions to navigate and extract data
  5. The result tree is serialized to the output format

This model is pull-based and declarative: you define what to do when you encounter each type of node, and the processor handles traversal.

A Complete XSLT Stylesheet

xml
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html" indent="yes"/>

  <xsl:template match="/">
    <html>
      <head>
        <title>Book Catalog</title>
        <style>
          table { border-collapse: collapse; width: 100%; }
          th, td { border: 1px solid #ccc; padding: 8px; }
          .expensive { background: #fff3cd; }
        </style>
      </head>
      <body>
        <h1>Book Catalog</h1>
        <table>
          <tr><th>Title</th><th>Author</th><th>Price</th></tr>
          <xsl:apply-templates select="catalog/book">
            <xsl:sort select="price" data-type="number" order="ascending"/>
          </xsl:apply-templates>
        </table>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="book">
    <tr>
      <xsl:if test="price > 15">
        <xsl:attribute name="class">expensive</xsl:attribute>
      </xsl:if>
      <td><xsl:value-of select="title"/></td>
      <td><xsl:value-of select="author"/></td>
      <td>$<xsl:value-of select="price"/></td>
    </tr>
  </xsl:template>

</xsl:stylesheet>

Core XSLT Elements

Output and Templates

`<xsl:output method="">` — declares the output format: method="html", method="xml", or method="text". Add indent="yes" for pretty-printed output.

`<xsl:template match="XPath">` — a rule that fires when the processor encounters a matching node. The match attribute accepts XPath patterns: match="/" (document root), match="book" (any book element), match="book[@genre='fiction']" (fiction books only).

`<xsl:apply-templates select="XPath"/>` — hands off processing to matching templates. The recursive heart of XSLT — each template processes its nodes and calls apply-templates for children.

Data Extraction

`<xsl:value-of select="XPath"/>` — outputs the string value of the selected node. The primary data extraction instruction. Escapes special characters automatically.

`<xsl:copy-of select="XPath"/>` — deep-copies a node including all children and attributes. Use when you want to reproduce part of the input unchanged.

Iteration and Conditionals

`<xsl:for-each select="XPath">` — iterates over every node in the selection. Within the loop, the context node shifts to each selected node in turn.

xml
<xsl:for-each select="catalog/book">
  <xsl:sort select="author" order="ascending"/>
  <li><xsl:value-of select="title"/> by <xsl:value-of select="author"/></li>
</xsl:for-each>

`<xsl:if test="condition">` — conditional output. No else clause.

`<xsl:choose>/<xsl:when>/<xsl:otherwise>` — multi-branch conditional (if/else if/else):

xml
<xsl:choose>
  <xsl:when test="price > 20">premium</xsl:when>
  <xsl:when test="price > 10">standard</xsl:when>
  <xsl:otherwise>budget</xsl:otherwise>
</xsl:choose>

Dynamic Output

`<xsl:attribute name="">` — generates an attribute on the current output element:

xml
<a>
  <xsl:attribute name="href">
    <xsl:value-of select="concat('/books/', @id)"/>
  </xsl:attribute>
  <xsl:value-of select="title"/>
</a>

`<xsl:variable name="" select="">` — declares a read-only variable (XSLT 1.0 variables cannot be reassigned):

xml
<xsl:variable name="book-count" select="count(//book)"/>
<p>Total: <xsl:value-of select="$book-count"/> books</p>

Template Matching vs. for-each

This is the fundamental XSLT design choice.

`<xsl:for-each>` is imperative — "loop through these nodes and do this." Familiar to programmers, reads like a for-loop, but does not scale well to complex transformations.

Template matching (<xsl:apply-templates>) is declarative — define what to do with each node type, and let the processor traverse. More modular, more maintainable for complex transformations. Each template has one responsibility.

Best practice: use templates for the overall document structure, use for-each for simple lists within a template.

The Identity Transform

The identity transform copies input to output unchanged — useful as a base you modify:

xml
<xsl:template match="@*|node()">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>

Apply this first, then add specific templates that override it for elements you want to change. This is the XSLT equivalent of method overriding.

Running XSLT

Browser: link stylesheet to XML with a processing instruction:

xml
<?xml-stylesheet type="text/xsl" href="catalog.xsl"?>

Command line (Linux/Mac):

bash
xsltproc catalog.xsl data.xml > output.html

Python:

python
from lxml import etree
transform = etree.XSLT(etree.parse("catalog.xsl"))
result = transform(etree.parse("data.xml"))
with open("output.html", "wb") as f:
    f.write(bytes(result))

Java — built into the JDK via javax.xml.transform.TransformerFactory.

JavaScript (browser):

javascript
const xslt = new XSLTProcessor();
xslt.importStylesheet(stylesheetDocument);
const result = xslt.transformToDocument(inputDocument);

XSLT in Modern Workflows

XSLT may seem dated, but it appears in more modern contexts than you might expect:

  • Report generation: transform XML data exports from databases, ERP systems, or IoT platforms into styled HTML reports
  • Data migration: convert XML from one schema format to another when integrating systems (common in healthcare HL7 and financial FIX protocol migrations)
  • Build pipelines: Maven POM processing, DocBook → HTML publishing pipelines
  • XSLT 3.0 (Saxon): adds streaming for large documents, JSON input/output, higher-order functions, and maps — making it relevant for modern data engineering

The declarative template model in XSLT is conceptually identical to the transformation pipelines in Power Query, n8n, and data engineering tools — input, rules, output. Understanding XSLT's model builds good instincts for data transformation at every level.

See [XML in the Wild](/tutorials/xml-fundamentals/xml-in-the-wild) for where XSLT is used in real production systems, and the [XML, XPath & XSLT Reference](/tutorials/xml-fundamentals/reference/xml-reference) for the complete element and function reference.

Example

xml
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html" indent="yes"/>

  <!-- Root template -->
  <xsl:template match="/">
    <ul>
      <xsl:apply-templates select="catalog/book">
        <xsl:sort select="price" data-type="number" order="descending"/>
      </xsl:apply-templates>
    </ul>
  </xsl:template>

  <!-- Book template -->
  <xsl:template match="book">
    <li>
      <xsl:value-of select="title"/>
      <xsl:text> — $</xsl:text>
      <xsl:value-of select="price"/>
      <xsl:if test="@genre='fiction'">
        <xsl:text> (fiction)</xsl:text>
      </xsl:if>
    </li>
  </xsl:template>
</xsl:stylesheet>
Try it yourself — XML