Advanced matching and sorting
Match pattern priorities, multi-key sorting with data types, key-based lookup, and grouping without a group-by instruction.
How the processor chooses a rule
Several templates can match the same node. XSLT resolves the conflict by priority: a more specific pattern wins, and explicit priority attributes settle ties. If two rules have equal priority, the one declared later wins.
| Pattern | Relative priority | Why |
|---|---|---|
book | 0 | A plain name test |
book[1] | 0.5 | A predicate makes it more specific |
catalog/book | 0.5 | A longer path step |
* | -0.5 | Matches anything, so it loses |
node() | -0.5 | Lowest of the built-in-style tests |
book[@id='bk-101'] | 0.5 | Predicate on an attribute |
Explicit priority="9" | 9 | You decide, overriding the default |
<xsl:template match="book[price > 50]" priority="3">
<tr class="expensive"><xsl:apply-templates/></tr>
</xsl:template>
<xsl:template match="book">
<tr><xsl:apply-templates/></tr>
</xsl:template>⚠️
Relying on declaration order is fragile: reordering templates silently changes output. When two patterns genuinely overlap, state the priority explicitly instead of trusting the tie-break.
Sorting with more than one key
<xsl:apply-templates select="book">
<xsl:sort select="price" data-type="number" order="ascending"/>
<xsl:sort select="title" lang="en" case-order="upper-first"/>
</xsl:apply-templates>
<!-- inside for-each, direct children of the current node are pre-sorted too -->
<xsl:for-each select="catalog/book">
<xsl:sort select="@id"/>
<xsl:value-of select="title"/>
</xsl:for-each>data-type="number"is essential: the default istext, so 100 sorts before 20.- Multiple
xsl:sortelements are applied in order, so the first is the primary key. langandcase-ordercontrol collation for accented and mixed-case text.- Sorting changes the processing order, not the document order used by position-based predicates.
Keys and grouping
A key builds an index across the source document, turning a repeated deep search into a hash lookup. Grouping has no dedicated instruction in XSLT 1.0 — the standard technique is the Muenchian method, which uses a key plus generate-id() to find the first node of each group.
<xsl:key name="books-by-genre" match="book" use="genre"/>
<xsl:template match="catalog">
<!-- one row per distinct genre -->
<xsl:for-each select="book[generate-id() =
generate-id(key('books-by-genre', genre)[1])]">
<h2><xsl:value-of select="genre"/></h2>
<ul>
<!-- every book in that genre -->
<xsl:for-each select="key('books-by-genre', genre)">
<li><xsl:value-of select="title"/></li>
</xsl:for-each>
</ul>
</xsl:for-each>
</xsl:template><!-- XSLT 2.0 and later replace all of the above with two lines -->
<xsl:for-each-group select="book" group-by="genre">
<h2><xsl:value-of select="current-grouping-key()"/></h2>
<ul>
<xsl:for-each select="current-group()">
<li><xsl:value-of select="title"/></li>
</xsl:for-each>
</ul>
</xsl:for-each-group>FAQ
Why is my numeric sort wrong?
The default
data-type is text, so values are compared as strings. Add data-type="number" on every sort key whose value is numeric.When should I use a key?
Whenever the same lookup repeats, or when you would otherwise scan with
// from the current node. One pass builds the index and every subsequent lookup is constant time.Related
Templates and apply-templates Predicates and functions
Last refreshed 2026-09-18.