When several rules match, the highest import precedence (Overriding and Importing) wins, then the highest priority. Defaults: book 0, bn:* -0.25, * -0.5, and patterns with predicates or paths 0.5. xsl:next-match hands the node to the next-best rule, so rules can wrap each other, and a mode is a separate rule set. Run xslt3 -s:booknest-catalog.xml -xsl:modes.xsl:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
expand-text="yes">
<xsl:output method="text"/>
<xsl:template match="/catalog">
<xsl:apply-templates select="book" mode="toc"/>
<xsl:text> </xsl:text>
<xsl:apply-templates select="book[position() le 3]"/>
</xsl:template>
<xsl:template match="book" mode="toc">{@id} </xsl:template>
<xsl:template match="book">{title} </xsl:template>
<xsl:template match="book[rating > 4.7]">Top rated: <xsl:next-match/></xsl:template>
<xsl:template match="book[pages > 400]" priority="1">Long: <xsl:next-match/></xsl:template>
</xsl:stylesheet>Output
b1 b2 b3 b4 b5 b6 The Quiet Harbor Long: Patterns of the Deep Web Top rated: Salt and Saffron
Both predicate rules have priority 0.5. Without priority="1", a book matching both (try rating > 4.2) makes Saxon 726,956 warn XTDE0540 Ambiguous rule match and use the rule declared last. xsl:mode sets what happens to unmatched nodes (on-no-match="shallow-copy", "shallow-skip", "fail").
Multiple Templates
<xsl:apply-templates>
<xsl:apply-templates> is similar to <xsl:value-of> in that it replaces itself with data, but where <xsl:value-of> copies data from an XPath expression evaluated in the current context, <xsl:apply-templates> copies data produced by whichever template rule matches the selected nodes. Its select attribute is optional; when omitted, the children of the context node are processed. The example below applies a shared template to both <Name> and <Age> elements, inserting a comma between sibling values.<?xml version="1.0" encoding="UTF-8"?>
<Root>
<Employee>
<Name>Dash</Name>
<Age>23</Age>
</Employee>
<Employee>
<Name>Gwen</Name>
<Age>22</Age>
</Employee>
</Root>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/Root">
<Root>
<Employee>
<Name>
<xsl:apply-templates select="Employee/Name"/>
</Name>
<Age>
<xsl:apply-templates select="Employee/Age"/>
</Age>
</Employee>
</Root>
</xsl:template>
<xsl:template match="Name|Age">
<xsl:value-of select="."/>
<xsl:if test="position()!=last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Output
<?xml version="1.0" encoding="UTF-8"?>
<Root>
<Employee>
<Name>Dash,Gwen</Name>
<Age>23,22</Age>
</Employee>
</Root>
<xsl:apply-templates> can contain <xsl:sort> and <xsl:with-param> as child elements, and can carry a mode (token) attribute. It can also be used to execute a multi-staged pipeline of transformations, as in the example below, which sorts a tree of directory/file nodes and then discards deeper nesting by re-matching inner nodes against a more specific pattern.
<?xml version="1.0" encoding="UTF-8"?>
<treeview>
<treenode>
<caption>Directory Z</caption>
<nodes>
<treenode><caption>File B</caption></treenode>
<treenode><caption>File Z</caption></treenode>
<treenode><caption>File A</caption></treenode>
</nodes>
</treenode>
<treenode>
<caption>Directory G</caption>
<nodes>
<treenode><caption>File F</caption></treenode>
<treenode><caption>File O</caption></treenode>
<treenode><caption>File B</caption></treenode>
</nodes>
</treenode>
</treeview>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<treeview>
<xsl:apply-templates select="treeview/treenode">
<xsl:sort select="caption" data-type="text"/>
</xsl:apply-templates>
</treeview>
</xsl:template>
<xsl:template match="treenode">
<treenode>
<xsl:copy-of select="caption"/>
<nodes>
<xsl:apply-templates select="nodes/treenode">
<xsl:sort select="caption" data-type="text"/>
</xsl:apply-templates>
</nodes>
</treenode>
</xsl:template>
<xsl:template match= "/treeview/treenode/nodes/treenode">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
Output
<?xml version="1.0" encoding="UTF-8"?>
<treeview>
<treenode>
<caption>Directory G</caption>
<nodes>
<treenode>
<caption>File B</caption>
</treenode>
<treenode>
<caption>File F</caption>
</treenode>
<treenode>
<caption>File O</caption>
</treenode>
</nodes>
</treenode>
<treenode>
<caption>Directory Z</caption>
<nodes>
<treenode>
<caption>File A</caption>
</treenode>
<treenode>
<caption>File B</caption>
</treenode>
<treenode>
<caption>File Z</caption>
</treenode>
</nodes>
</treenode>
</treeview>
<xsl:next-match>
<xsl:next-match> is similar to <xsl:apply-templates> in that it invokes another matching template, but unlike <xsl:apply-templates>, which builds on the current context, <xsl:next-match> resets the context and excludes the current template rule from the next match. Note that this makes it possible to build a recursive chain of templates, as shown below where an element with several boolean attributes is progressively wrapped by the templates matching each attribute, in priority order.<?xml version="1.0" encoding="UTF-8"?>
<element bold="true"
subscript="false"
italic="true"
text="stuff"/>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="element" priority="1">
<xsl:value-of select="@text" />
</xsl:template>
<xsl:template match="element[@italic = 'true']"
priority="2">
<sup><xsl:next-match/></sup>
</xsl:template>
<xsl:template match="element[@subscript = 'true']"
priority="3">
<sub><xsl:next-match/></sub>
</xsl:template>
<xsl:template match="element[@bold = 'true']"
priority="4">
<strong><xsl:next-match/></strong>
</xsl:template>
</xsl:stylesheet>
Output
<?xml version="1.0" encoding="UTF-8"?> <strong> <sup>stuff</sup> </strong>
<xsl:call-template>, <xsl:context-item>
<xsl:call-template> invokes a named template directly, independent of the current node's matching rules, passing parameters with <xsl:with-param>.<?xml version="1.0" encoding="UTF-8"?>
<a>
<b>1<c/>a</b>
<b>2<c/>b</b>
<b>3<c/>c</b>
<b>4<c/>d</b>
</a>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:template name="T">
<xsl:context-item as="node()"/>
<xsl:param name="P"/>
<xsl:apply-templates select="b[position() lt 3]"/>
<xsl:value-of select="$P"/>
</xsl:template>
<xsl:template match="a">
<xsl:call-template name="T">
<xsl:with-param name="P" select="'X'"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
Output
<?xml version="1.0" encoding="UTF-8"?>1a2bX
- use = "required" | "optional" | "prohibited"