Skip to content
datahacks.dev
XML

XSLT Basics With Templates Match and Apply Templates

How an XSLT processor decides what to run: template match patterns, apply-templates, the built-in rules that dump text, modes, and the identity template.

6 min read
Summarize in AI
Flat illustration of paired angle-bracket tags wrapped around a document, with the XML logo.

XSLT confuses people because it does not read like a program. There is no main function and no loop you write. A stylesheet is a set of rules, each saying "when you meet a node like this, write that", and the processor walks the input and applies the rules. Once that model is clear, the language is small. This article is about the model: how the processor chooses a rule, what happens to nodes without one, and the two patterns that most stylesheets are built from.

Templates and match patterns

A template has a match attribute, a pattern describing the nodes it applies to, and a body that says what to write.

titles.xslXML
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:template match="/"><xsl:apply-templates select="library/book"/></xsl:template>
  <xsl:template match="book"><xsl:value-of select="title"/><xsl:text>&#10;</xsl:text></xsl:template>
</xsl:stylesheet>
Text
Cache Patterns
SQL Tuning, 2nd Edition
Indexes "Explained"

The processor starts at the root node /. The first template matches it. Its body contains apply-templates select="library/book", which means: take these nodes, and for each, find the best matching template and run it. For each book that is the second template, which writes the title and a newline. The match pattern is a restricted XPath (paths and predicates going downward). The select in apply-templates is full XPath, evaluated against the current node.

Two ideas are in there. Pushing: the root template does not loop over books and format them; it hands them to the rules and the rules decide. Context: inside a template the current node is the matched node, so title in the second template means the title of this book.

When several templates match, the processor picks one by priority (more specific patterns win: book[@id='b1'] beats book, which beats *), and later templates beat earlier ones on a tie. You can set priority explicitly, but if you need to, the patterns are probably too similar.

The built-in rules

What if no template matches a node? XSLT defines default rules so that nothing is lost: for an element or the root, process the children; for a text node or an attribute, copy its text to the output. These rules explain the most confusing XSLT output of all. Take a stylesheet with one template for an element that does not occur:

empty.xslXML
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:template match="nothing"/>
</xsl:stylesheet>

Run it on the library file and the output is not empty:

Text
$
  $
    Cache Patterns$
    Ana Lee$
    Bo Chen$
    29.50$

(cat -A marks line ends with $.) The processor started at the root, found no template, applied the built-in rule (process the children), did the same for every element and, at each text node, copied the text. The result is all the document's text, with its indentation, and none of its tags or attributes. When a stylesheet "outputs the whole file as text", this is the cause: the templates did not match, so the built-in rules ran. The cure is to correct the pattern, usually because of a namespace or a path mistake (debugging an XSLT transform that outputs nothing).

The same rules explain a quieter thing: a template for book that writes the title, with no template for the root, still works, because the built-in rule for the root and for library descends until it reaches the books. The output also contains the whitespace text nodes between elements, which is why such stylesheets produce blank lines unless you add <xsl:strip-space elements="*"/>.

Modes

Sometimes the same nodes need two treatments. A mode is a name that groups templates, and apply-templates can ask for it:

modes.xslXML
<xsl:template match="/">
  <xsl:text>TOC:&#10;</xsl:text><xsl:apply-templates select="library/book" mode="toc"/>
  <xsl:text>DETAIL:&#10;</xsl:text><xsl:apply-templates select="library/book" mode="detail"/>
</xsl:template>
<xsl:template match="book" mode="toc"><xsl:value-of select="position()"/>. <xsl:value-of select="title"/><xsl:text>&#10;</xsl:text></xsl:template>
<xsl:template match="book" mode="detail"><xsl:value-of select="@id"/>: <xsl:value-of select="count(author)"/> author(s)<xsl:text>&#10;</xsl:text></xsl:template>
Text
TOC:
1. Cache Patterns
2. SQL Tuning, 2nd Edition
3. Indexes "Explained"
DETAIL:
b1: 2 author(s)
b2: 1 author(s)
b3: 1 author(s)

Both templates match book, and the mode decides which runs. position() inside the first is the position in the node list the apply-templates selected, which gives the numbering. Modes are how a table of contents and a body come from one source, and how a stylesheet does two passes without two files.

The identity template

Most real stylesheets do not build output from nothing. They copy the input and change a few things. The idiom is a template that matches every node and attribute and copies it:

id.xslXML
<xsl:template match="@*|node()">
  <xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy>
</xsl:template>
<xsl:template match="note"/>
<xsl:template match="price">
  <cost currency="{@currency}"><xsl:value-of select="."/></cost>
</xsl:template>

@*|node() matches any attribute or node. xsl:copy copies the current node (shallowly: the element without its children), and the apply-templates inside processes the attributes and children, which come back to the same template, so the whole tree is copied recursively. The two templates after it are overrides, and they win because they are more specific than @*|node(). The first has an empty body, which means "output nothing for this node": every note and its content disappear. The second rewrites each price into a cost element, moving the currency into an attribute through an attribute value template ({@currency}).

XML
<book id="b1" available="true">
  <title>Cache Patterns</title>
  <author>Ana Lee</author>
  <author>Bo Chen</author>
  <cost currency="USD">29.50</cost>

</book>

The output is the original with only those two changes. The empty line where the note was is the whitespace text node that stood around it; xsl:strip-space or xsl:output indent handles it. This is the pattern for renaming, removing and rewriting parts of a document, and it is far shorter than writing every element by hand. It is also safe against additions: new elements the stylesheet has never heard of are copied through unchanged.

Reading a stylesheet

To read one, find the templates and ask of each: what does it match, and what does it write? Then follow the apply-templates calls to see which template handles the children. Draw the flow if it is complicated. The usual faults are a match that is too narrow or in the wrong namespace (nothing matches and the built-in rules take over), an apply-templates with a select that finds nothing (the rule never runs), and a template writing text with surrounding whitespace you did not mean.

For a first stylesheet see using XSLT to transform XML into another shape; for grouping and sorting, grouping records with XSLT 2 and Muenchian keys and sorting and filtering XML with XSLT; the cluster overview is XSLT and XQuery for real transformations. The model is defined in the XSLT 1.0 specification, section 5. The patterns are a subset of XPath defined in section 5.2, and the built-in rules in section 5.8. Test an XPath in the XPath tester.

When templates are the wrong way to think about the job

The task is a computation. Totals, string processing and decisions with many branches read better in a language with variables and loops. XSLT can do them and the code gets tortuous.

You are writing output text with heavy formatting. Building CSV or SQL with templates means managing escaping and whitespace by hand.

The structure is regular and flat. A table of records is a loop in any language, and a stylesheet is more machinery than needed.

The team does not know XSLT. A rule-based language that nobody can read is a maintenance risk. Use it where its strength, restructuring trees, is the whole point.

Also inTooling
Share

Keep reading

Get new articles by email

We send one email when a new article is published. No spam.

Unsubscribe in one click. No spam, ever.