Match patterns versus select expressions
A stylesheet contains two different XPath dialects, and confusing them produces templates that never fire or select far more than intended.
Two dialects, one syntax
match on a template | select on apply-templates / value-of | |
|---|---|---|
| Purpose | Describe which nodes this template handles | Choose nodes to process or output |
| Evaluation | Pattern matching against one node at a time | A real XPath expression from the context node |
| Relative to | The whole document | The current context node |
| Return value | Must be a pattern | Any XPath type |
| Functions allowed | Only id() and key() | All of them |
| Order matters | No, priority decides | Yes, step order decides |
<xsl:template match="item"> <!-- fires for any item element -->
<xsl:template match="list/item"> <!-- only items whose parent is list -->
<xsl:template match="item[@status='new']">
<xsl:template match="//item"> <!-- legal in 1.0 only as id()/key() sugar;
prefer match="item", which also matches at any depth -->
<xsl:apply-templates select="item[price > 20]"/> <!-- select is a real expression -->
<xsl:apply-templates select="item" mode="summary"/>A pattern is matched against a node with the document as the starting point, so match="list/item" means "an item whose parent is a list", regardless of how the node was reached. A select is evaluated with the current node as the context, so select="list/item" inside a template that matches the root means something completely different.
The context item, position and last()
<xsl:template match="li">
<!-- position() is the position among the nodes being processed by apply-templates -->
<xsl:if test="position() = 1">
<p>First item: <xsl:value-of select="."/></p>
</xsl:if>
<xsl:if test="position() = last()">
<p>Last item: <xsl:value-of select="."/></p>
</xsl:if>
</xsl:template>position()depends on the node-list passed toapply-templates, not on the element's index in the document.- In XSLT 1.0
current()returns the node being matched at the outermost level, which is how you reference it inside a predicate. - In XSLT 2.0 and later,
current()refers to the current template rule's item, and.is the context item — they diverge insidefor-each. last()inside a predicate likeitem[last()]is the size of the node-list being filtered, not the size of the whole document.
<!-- current() keeps the outer node available while a predicate moves the focus -->
<xsl:template match="product">
<xsl:value-of select="//price[@sku = current()/@sku]"/>
</xsl:template>💡
Priorities resolve most conflicts, not document order: a pattern with a predicate or a longer path beats a shorter one. When two patterns have equal priority the later declaration wins, which is why a catch-all
match="*" belongs at the bottom of the file.FAQ
Why is my template never called?
Usually because nothing applies templates to that node. A template only runs when apply-templates reaches a matching node, or when the node is the initial node and matches the root pattern.
Can I put a function call in a match attribute?
No. Patterns allow only id() and key(), and even those are restricted in form. Move the logic into a select expression or a variable instead.
Related
Templates and apply-templates Advanced matching and sorting
Last refreshed 2026-09-18.