Basic Flow Control

<xsl:stylesheet>, <xsl:output>, <xsl:template>, <xsl:variable>, <xsl:for-each>, <xsl:value-of>, <xsl:copy-of>, <xsl:copy>

The example below defines a template matching the root element <a>, builds a <head> fragment in a variable, and iterates over the <b> children whose position is greater than another variable's value.

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="a.xslt"?>
<a>
   <b>1<c>Cat</c></b>
   <b>2<c>Cow</c></b>
   <b>3<c>Dog</c></b>
   <b>4<c>Fish</c></b>
</a>

<?xml version="1.0" encoding="UTF-8"?>
<!--a.xslt -->
<xsl:stylesheet version="1.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:output method="xml" version="1.0"
                     encoding="UTF-8" indent="yes"/>

   <xsl:template match="a">
      <xsl:variable name="v" as="xs:integer" select="2"/>
      <xsl:variable name="header" as="node()*">
         <head>
             <title>A B C</title>
          </head>
      </xsl:variable>
      <html>
         <xsl:copy-of select="$header"/>
         <body>
            <table><xsl:for-each select="b[position() > $v]">
               <tr><td><xsl:value-of select="text()"/></td>
                      <td><xsl:value-of select="c"/></td>
               </tr>
            </xsl:for-each></table>
         </body>
      </html>
   </xsl:template>
</xsl:stylesheet>

A B C
3 Dog
4 Fish
<xsl:stylesheet> defines a stylesheet module. <xsl:transform> is a synonym. It can have the following attributes:
<xsl:template>, <xsl:variable>, <xsl:param>, <xsl:function> and similar declarations can carry a visibility attribute:
<xsl:output> can have the following attributes: name (eqname), method ("xml" | "html" | "xhtml" | "text" | eqname), byte-order-mark, cdata-section-elements, doctype-public, doctype-system, encoding, escape-uri-attributes, html-version, include-content-type, indent, item-separator, media-type, normalization-form ("NFC" | "NFD" | "NFKC" | "NFKD" | "fully-normalized" | "none" | nmtoken), omit-xml-declaration, parameter-document, standalone ("yes" | "no" | "omit"), suppress-indentation, undeclare-prefixes, use-character-maps, version.
<xsl:output method="xml" indent="yes"/>

Use this setting to have XML or HTML output properly indented.


<xsl:template> can also carry mode and priority attributes. When a selected item matches more than one template rule under a given mode, only one rule is evaluated for that item; a rule with a higher priority is preferred.

<xsl:value-of> can carry a separator attribute, defining the string placed between adjacent items when the selected value is a sequence.

<xsl:copy-of> performs a deep copy, while <xsl:copy> performs a shallow copy. The function deep-copy($nodes) can also be used to perform a deep copy of nodes.

<xsl:copy-of> attributes: copy-namespaces ("yes" | "no"), type (eqname), validation ("strict" | "lax" | "preserve" | "strip").

<xsl:copy> attributes: copy-namespaces, inherit-namespaces, use-attribute-sets, type, validation, on-empty.

<xsl:variable> and <xsl:param> can carry a static attribute; when true, the variable or parameter becomes global and can also be used in <xsl:use-when> expressions.

Whenever the select attribute is absent from an instruction, the value is instead given as the instruction's child content.

<xsl:where-populated>, <xsl:on-non-empty>, <xsl:on-empty>

These instructions conditionally suppress output that would otherwise be empty. <xsl:where-populated> looks at content generated by its children, while <xsl:on-non-empty> looks at content generated by its siblings.

……
<div id="students">
<xsl:where-populated>
   <table>
      <xsl:on-non-empty>
         <thead>
            <tr><th>Name</th><th>Age</th></tr>
         </thead>
      </xsl:on-non-empty>
      <xsl:where-populated>
         <tbody>
            <xsl:for-each select="student/copy-of()">
               <tr>
                  <td><xsl:value-of select="name"/></td>
                  <td><xsl:value-of select="age"/></td>
               </tr>
            </xsl:for-each>
         </tbody>
      </xsl:where-populated>
   </table>
</xsl:where-populated>
<xsl:on-empty>
   <p>There are no students</p>
</xsl:on-empty>
</div>
……
In this fragment (courtesy of https://www.w3.org/TR/xslt-30/#where-populated-example):

<xsl:for-each-group>

The example below groups test scores by subject and outputs the average score per subject.

<?xml version="1.0" encoding="UTF-8"?>
<testScores>
   <test>
      <subject>Math</subject>
      <score>78</score>
   </test>
   <test>
      <subject>English</subject>
      <score>90</score>
   </test>
   <test>
      <subject>Malay</subject>
      <score>59</score>
   </test>
   <test>
      <subject>Math</subject>
      <score>95</score>
   </test>
   <test>
      <subject>English</subject>
      <score>85</score>
   </test>
</testScores>

<?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 match="testScores">
      <xsl:for-each-group
              select="test"
              group-by="subject">
         <xsl:value-of select="current-grouping-key()"/>
         <xsl:value-of select="avg(current-group()/score)"/>
      </xsl:for-each-group>
   </xsl:template>
</xsl:stylesheet>

Math86.5English87.5Malay59
<xsl:for-each-group> can have the following attributes: group-adjacent (expression), group-starting-with (pattern), group-ending-with (pattern), bind-group (eqname), bind-grouping-key (eqname), composite ("yes" | "no"), collation ({uri}).

<xsl:iterate>, <xsl:next-iteration>, <xsl:break>, <xsl:on-completion>, <xsl:param>, <xsl:choose>, <xsl:when>, <xsl:otherwise>

Unlike <xsl:for-each>, processing with <xsl:iterate> is explicitly sequential, which lets each iteration see values computed by the previous one through a tunnel-style parameter. The example below walks a list of primes, printing each prime together with the gap ("leap") since the previous one.

<?xml version="1.0" encoding="UTF-8"?>
<primes>
   <i>2</i>
   <i>3</i>
   <i>5</i>
   <i>7</i>
   <i>11</i>
   <i>13</i>
</primes>

<?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:output method="xml" version="1.0"
                     encoding="UTF-8" indent="yes"/>
   <xsl:template match="/primes">
      <xsl:iterate select="i">
         <xsl:param name="cnt" select="1"/>
         <xsl:on-completion>
             Primes are fun.
         </xsl:on-completion>
         <xsl:choose>
            <xsl:when test="position()=1">
              &#10; Prime: 2
              <xsl:next-iteration>
                 <xsl:with-param name="cnt" select="$cnt+1"
                                          as="xs:integer"/>
              </xsl:next-iteration>
            </xsl:when>
            <xsl:when test="position() le 5">
              &#10; Prime <xsl:value-of select="."/>
              Leap: <xsl:value-of select=". - /*//i[$cnt - 1]"/>
              <xsl:next-iteration>
                <xsl:with-param name="cnt" select="$cnt+1"
                                         as="xs:integer"/>
              </xsl:next-iteration>
            </xsl:when>
            <xsl:otherwise>
               <xsl:break/>
            </xsl:otherwise>
         </xsl:choose>
      </xsl:iterate>
   </xsl:template>
</xsl:stylesheet>

Prime: 2 Prime 3 Leap: 1 Prime 5 Leap: 2 Prime 7 Leap: 2 Prime 11 Leap: 4
<xsl:param> can have the following attributes: <xsl:break> and <xsl:next-iteration> must be the last instruction in the <xsl:iterate> loop; each branch that continues the loop must therefore repeat its own <xsl:next-iteration>, as shown above.

<xsl:if>, <xsl:try>, <xsl:catch>, <xsl:function>, <xsl:message>

<xsl:try> evaluates an expression and lets <xsl:catch> handle any dynamic error raised, exposing details through the $err:code, $err:description, $err:value, $err:module, $err:line-number and $err:column-number variables. The example below deliberately triggers a division-by-zero error by calling an undefined function (ns:plus) inside the expression, which XSLT reports through the same error-handling mechanism.

<?xml version="1.0" encoding="UTF-8"?>
<division>
  <num1>9999</num1>
  <num2>9</num2>
</division>

<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform 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"
      xmlns:err="http://www.w3.org/2005/xqt-errors"
      xmlns:ns ="http://example.com">

   <xsl:function name="ns:divide" as="xs:decimal">
     <xsl:param name="x" as="xs:double"/>
     <xsl:param name="y" as="xs:double"/>
     <xsl:value-of select="$x div $y"/>
   </xsl:function>

   <xsl:template match="division">
      <xsl:try select=
                "xs:integer(ns:plus(num1,num2)) div xs:integer(num2)">
         <xsl:catch errors="*">
              <!--<xsl:catch errors="err:FOAR0001"> -->
              <xsl:message>
                Code: <xsl:value-of select="$err:code"/>
                Description: <xsl:value-of select="$err:description"/>
                Value: <xsl:value-of select="$err:value"/>
                Module: <xsl:value-of select="$err:module"/>
                Line-number: <xsl:value-of select="$err:line-number"/>
                Column-number:
                                 <xsl:value-of select="$err:column-number"/>
           </xsl:message>
         </xsl:catch>
      </xsl:try>
   </xsl:template>
</xsl:transform>

Code: err:FOAR0001 Description: Division by zero Value: Module: x.xslt Line-number: 17 Column-number: -1
<xsl:try> or <xsl:catch> can carry the select attribute; if omitted, the value is instead given as content children.

A custom function, defined with <xsl:function>, must use a namespace.

<xsl:message> can carry the terminate ("yes" | "no") and error-code attributes.

<xsl:function> can have the following attributes: visibility ("public" | "private" | "final" | "abstract"), override-extension-function ("yes" | "no"), identity-sensitive ("yes" | "no"), cache ("full" | "partial" | "no").

Run examples that use <xsl:message> from the command line, or with an IDE such as oXygen, to see the message output.