8 Conditional Processing

There are two instructions in XSLT that support conditional processing: xsl:if and xsl:choose. The xsl:if instruction provides simple if-then conditionality; the xsl:choose instruction supports selection of one choice when there are several possibilities.

XSLT 3.0 also supports xsl:try and xsl:catch which define conditional processing to handle dynamic errors.

8.1 Conditional Processing with xsl:if

<!-- Category: instruction -->
<xsl:if
  test = expression >
  <!-- Content: sequence-constructor -->
</xsl:if>

The xsl:if element has a mandatory test attribute, which specifies an expression. The content is a sequence constructor.

The result of the xsl:if instruction depends on the effective boolean valueXP30 of the expression in the test attribute. The rules for determining the effective boolean value of an expression are given in [XPath 3.0]: they are the same as the rules used for XPath conditional expressions.

If the effective boolean value of the expression is true, then the sequence constructor is evaluated (see 5.7 Sequence Constructors), and the resulting sequence is returned as the result of the xsl:if instruction; otherwise, the sequence constructor is not evaluated, and the empty sequence is returned.

Example: Using xsl:if

In the following example, the names in a group of names are formatted as a comma separated list:

<xsl:template match="namelist/name">
  <xsl:apply-templates/>
  <xsl:if test="not(position()=last())">, </xsl:if>
</xsl:template>

The following colors every other table row yellow:

<xsl:template match="item">
  <tr>
    <xsl:if test="position() mod 2 = 0">
       <xsl:attribute name="bgcolor">yellow</xsl:attribute>
    </xsl:if>
    <xsl:apply-templates/>
  </tr>
</xsl:template>

8.2 Conditional Processing with xsl:choose

<!-- Category: instruction -->
<xsl:choose>
  <!-- Content: (xsl:when+, xsl:otherwise?) -->
</xsl:choose>

<xsl:when
  test = expression >
  <!-- Content: sequence-constructor -->
</xsl:when>

<xsl:otherwise>
  <!-- Content: sequence-constructor -->
</xsl:otherwise>

The xsl:choose element selects one among a number of possible alternatives. It consists of a sequence of one or more xsl:when elements followed by an optional xsl:otherwise element. Each xsl:when element has a single attribute, test, which specifies an expression. The content of the xsl:when and xsl:otherwise elements is a sequence constructor.

When an xsl:choose element is processed, each of the xsl:when elements is tested in turn (that is, in the order that the elements appear in the stylesheet), until one of the xsl:when elements is satisfied. If none of the xsl:when elements is satisfied, then the xsl:otherwise element is considered, as described below.

An xsl:when element is satisfied if the effective boolean valueXP30 of the expression in its test attribute is true. The rules for determining the effective boolean value of an expression are given in [XPath 3.0]: they are the same as the rules used for XPath conditional expressions.

The content of the first, and only the first, xsl:when element that is satisfied is evaluated, and the resulting sequence is returned as the result of the xsl:choose instruction. If no xsl:when element is satisfied, the content of the xsl:otherwise element is evaluated, and the resulting sequence is returned as the result of the xsl:choose instruction. If no xsl:when element is satisfied, and no xsl:otherwise element is present, the result of the xsl:choose instruction is an empty sequence.

Only the sequence constructor of the selected xsl:when or xsl:otherwise instruction is evaluated. The test expressions for xsl:when instructions after the selected one are not evaluated.

Example: Using xsl:choose

The following example enumerates items in an ordered list using arabic numerals, letters, or roman numerals depending on the depth to which the ordered lists are nested.

<xsl:template match="orderedlist/listitem">
  <fo:list-item indent-start='2pi'>
    <fo:list-item-label>
      <xsl:variable name="level"
                    select="count(ancestor::orderedlist) mod 3"/>
      <xsl:choose>
        <xsl:when test='$level=1'>
          <xsl:number format="i"/>
        </xsl:when>
        <xsl:when test='$level=2'>
          <xsl:number format="a"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:number format="1"/>
        </xsl:otherwise>
      </xsl:choose>
      <xsl:text>. </xsl:text>
    </fo:list-item-label>
    <fo:list-item-body>
      <xsl:apply-templates/>
    </fo:list-item-body>
  </fo:list-item>
</xsl:template>

8.3 Try/Catch

The xsl:try instruction can be used to trap dynamic errors occurring within the expression it wraps; the recovery action if such errors occur is defined using a child xsl:catch element.

<!-- Category: instruction -->
<xsl:try
  select? = expression
  rollback-output? = boolean >
  <!-- Content: (sequence-constructor, xsl:catch, (xsl:catch | xsl:fallback)*) -->
</xsl:try>

Note:

Because a sequence constructor may contain an xsl:fallback element, the effect of this content model is that an xsl:fallback instruction may appear as a child of xsl:try in any position.

<xsl:catch
  errors? = tokens
  select? = expression >
  <!-- Content: sequence-constructor -->
</xsl:catch>

An xsl:try instruction evaluates either the expression contained in its select attribute, or its contained sequence constructor, and returns the result of that evaluation if it succeeds without error. If a dynamic error occurs during the evaluation, the processor evaluates the first xsl:catch child element applicable to the error, and returns that result instead.

If the xsl:try element has a select attribute, then it must have no children other than xsl:catch and xsl:fallback. That is, the select attribute and the contained sequence constructor are mutually exclusive. If neither is present, the result of the xsl:try is an empty sequence (no dynamic error can occur in this case).

The rollback-output attribute is described in 8.3.1 Recovery of Result Trees. The default value is yes.

[ERR XTSE3140] It is a static error if the select attribute of the xsl:try element is present and the element has children other than xsl:catch and xsl:fallback elements.

Any xsl:fallback children of the xsl:try element are ignored by an XSLT 3.0 processor, but can be used to define the recovery action taken by an XSLT 1.0 or XSLT 2.0 processor operating with forwards compatible behavior.

The xsl:catch element has an optional errors attribute, which lists the error conditions that the xsl:catch element is designed to intercept. The default value is errors="*", which catches all errors. The value is a whitespace-separated list of NameTestsXP30; an xsl:catch element catches an error condition if this list includes a NameTest that matches the error code associated with that error condition.

Note:

Error codes are QNames. Those defined in this specification and in related specifications are all in the standard error namespace, and may therefore be caught using an xsl:catch element such as <xsl:catch errors="err:FODC0001 err:FODC0005"> where the namespace prefix err is bound to this namespace. Errors defined by implementers, and errors raised by an explicit call of the errorFO30 function or by use of the xsl:message or xsl:assert instruction, may use error codes in other namespaces.

If more than one xsl:catch element matches an error, the error is processed using the first one that matches, in document order. If no xsl:catch matches the error, then the error is not caught (that is, evaluation of the xsl:try element fails with the dynamic error).

An xsl:catch element may have either a select attribute, or a contained sequence constructor.

[ERR XTSE3150] It is a static error if the select attribute of the xsl:catch element is present unless the element has empty content.

The result of evaluating the xsl:catch element is the result of evaluating the XPath expression in its select attribute or the result of evaluating the contained sequence constructor; if neither is present, the result is an empty sequence. This result is delivered as the result of the xsl:try instruction.

If a dynamic error occurs during the evaluation of xsl:catch, it causes the containing xsl:try to fail with this error. The error is not caught by other sibling xsl:catch elements within the same xsl:try instruction, but it may be caught by an xsl:try instruction at an outer level, or by an xsl:try instruction nested within the xsl:catch.

Within the select expression, or within the sequence constructor contained by the xsl:catch element, a number of variables are implicitly declared, giving information about the error that occurred. These are lexically scoped to the xsl:catch element. These variables are all in the standard error namespace, and they are initialized as described in the following table:

Variables Available within xsl:catch
Variable Type Value
err:code xs:QName The error code
err:description xs:string? A description of the error condition; an empty sequence if no description is available (for example, if the errorFO30 function was called with one argument).
err:value item()* Value associated with the error. For an error raised by calling the errorFO30 function, this is the value of the third argument (if supplied). For an error raised by evaluating xsl:message with terminate="yes", or a failing xsl:assert, this is the document node at the root of the tree containing the XML message body.
err:module xs:string? The URI (or system ID) of the stylesheet module containing the instruction where the error occurred; an empty sequence if the information is not available.
err:line-number xs:integer? The line number within the stylesheet module of the instruction where the error occurred; an empty sequence if the information is not available. The value may be approximate.
err:column-number xs:integer? The column number within the stylesheet module of the instruction where the error occurred; an empty sequence if the information is not available. The value may be approximate.

Variables declared within the sequence constructor of the xsl:try element (and not within an xsl:catch) are not visible within the xsl:catch element.

Note:

Within an xsl:catch it is possible to re-throw the error using the function call error($err:code, $err:description, $err:value).

The following additional rules apply to the catching of errors:

  1. All dynamic errors occurring during the evaluation of the xsl:try sequence constructor or select expression are caught (provided they match one of the xsl:catch elements).

    Note:

    • This includes errors occurring in functions or templates invoked in the course of this evaluation, unless already caught by a nested xsl:try.

    • It also includes (for example) errors caused by calling the errorFO30 function, or the xsl:message instruction with terminate="yes", or the xsl:assert instruction, or the xs:error constructor function.

    • It does not include errors that occur while evaluating references to variables whose declaration and initialization is outside the xsl:try.

  2. The existence of an xsl:try instruction does not affect the obligation of the processor to signal certain errors as static errors, or its right to choose whether to signal some errors (such as type errors) statically or dynamically. Static errors are never caught.

  3. Some fatal errors arising in the processing environment, such as running out of memory, may cause termination of the transformation despite the presence of an xsl:try instruction. This is implementation-dependent.

  4. If the sequence constructor or select expression of the xsl:try causes execution of xsl:result-document, xsl:message, or xsl:assert instructions and fails with a dynamic error that is caught, it is implementation-dependent whether these instructions have any externally visible effect. The processor is not required to roll back any changes made by these instructions. The same applies to any side effects caused by extension functions or extension instructions.

  5. A serialization error that occurs during the serialization of a secondary result produced using xsl:result-document is treated as a dynamic error in the evaluation of the xsl:result-document instruction, and may be caught (for example by an xsl:try instruction that contains the xsl:result-document instruction). A serialization error that occurs while serializing the principal result is treated as occurring after the transformation has finished, and cannot be caught.

  6. A validation error is treated as occurring in the instruction that requested validation. For example, if the stylesheet is producing XHTML output and requests validation of the entire result document by means of the attribute validation="strict" on the instruction that creates the outermost html element, then a validation failure can be caught only at that level. Although the validation error might be detected, for example, while writing a p element at a location where no p element is allowed, it is not treated as an error in the instruction that writes the p element and cannot be caught at that level.

  7. A type error may be caught if the processor raises it dynamically; this does not affect the processor’s right to raise the error statically if it chooses.

    The following rules are provided to define which expression is considered to fail when a type error occurs, and therefore where the error can be caught. The general principle is that where the semantics of a construct C place requirements on the type of some subexpression, a type error is an error in the evaluation of C, not in the evaluation of the subexpression.

    For example, consider the following construct:

    <xsl:variable name="v" as="xs:integer">
      <xsl:sequence select="$foo"/>
    </xsl:variable>

    The expected type of the result of the sequence constructor is xs:integer; if the value of variable $foo turns out to be a string, then a type error will occur. It is not possible to catch this by writing:

    <xsl:variable name="v" as="xs:integer">
      <xsl:try>
        <xsl:sequence select="$foo"/>
        <xsl:catch>...</xsl:catch>
      </xsl:try>
    </xsl:variable>

    This fails to catch the error because the xsl:sequence instruction is deemed to evaluate successfully; the failure only occurs when the result of this instruction is bound to the variable.

    A similar rule applies to functions: if the body of a function computes a result which does not conform to the required type of the function result, it is not possible to catch this error within the function body itself; it can only be caught by the caller of the function. Similarly, if an expression used to compute an argument to a function returns a value of the wrong type for the function signature, this is not considered an error in this expression, but an error in evaluating the function call as a whole.

    A consequence of these rules is that when a type error occurs while initializing a global variable (because the initializer returns a value of the wrong type, given the declared type of the variable), then this error cannot be caught.

    Note:

    Because processors are permitted to report type errors during static analysis, it is unwise to attempt to recover from type errors dynamically. The best strategy is generally to prevent their occurrence. For example, rather than writing $p + 1 where $p is a parameter of unknown type, and then catching the type error that occurs if $p is not numeric, it is better first to test whether $p is numeric, perhaps by means of an expression such as $p instance of my:numeric, where my:numeric is a union type with xs:double, xs:float, and xs:decimal as its member types.

  8. The fact that the application tries to catch errors does not prevent the processor from organizing the evaluation in such a way as to prevent errors occurring. For example exists(//a[10 div . gt 5]) may still do an “early exit”, rather than examining every item in the sequence just to see if it triggers a divide-by-zero error.

  9. Except as specified above, the optimizer must not rearrange the evaluation (at compile time or at run time) so that expressions written to be subject to the try/catch are evaluated outside its scope, or expressions written to be external to the try/catch are evaluated within its scope. This does not prevent expressions being rearranged, but any expression that is so rearranged must carry its try/catch context with it.

8.3.1 Recovery of Result Trees

The XSLT language is designed so that a processor that chooses to execute instructions in document order will always append nodes to the result tree in document order, and never needs to update a result tree in situ. As a result, it is normal practice for XSLT processors to stream the result tree directly to its final destination (for example, a serializer) without ever holding the tree in memory. This applies whether or not the processor is streamable, and whether or not source documents are streamed.

The language specification states (see 2.14 Error Handling) that when a transformation terminates with a dynamic error, the state of persistent resources affected by the transformation (for example, serialized result documents) is implementation-defined, so processors are not required to take any special steps to recover such resources to their pre-transformation state; at the same time, there is no guarantee that secondary result documents produced before the failure occurs will be in a usable state.

The situation becomes more complicated when dynamic errors occur while writing to a result tree, and the dynamic error is caught by an xsl:try/xsl:catch instruction. The semantics of these instructions requires that when an error occurring during the evaluation of xsl:try is caught, the result of the xsl:try instruction is the result of the relevant xsl:catch. To achieve this, any output written to the result tree during the execution of xsl:try until the point where the error occurs must effectively be undone. There are two basic strategies for achieving this: either the updates are not committed to persistent storage until the xsl:try instruction is completed, or the updates are written in such a way that they can be rolled back in the event of a failure.

Both these strategies are potentially expensive, and both have an adverse effect on streaming, in that they affect the amount of memory needed to transform large amounts of data. XSLT 3.0 therefore provides an option to relax the requirement to recover result trees when failures occur in the course of evaluating an xsl:try instruction. This option is invoked by specifying rollback-output="no" on the xsl:try instruction.

The default value of the attribute is rollback-output="yes".

The effect of specifying rollback-output="no" on xsl:try is as follows: if a dynamic error occurs in the course of evaluating the xsl:try instruction, and if the failing construct is evaluated in final output state while writing to some result document, then it is implementation-dependent whether an attempt to catch this error using xsl:catch will be successful. If the attempt is successful, then the xsl:try instruction succeeds, delivering the result of evaluating the xsl:catch clause, and the transformation proceeds as normal. If the attempt is unsuccessful (typically, because non-recoverable updates have already been made to the result tree), then the xsl:try instruction as a whole fails with a dynamic error. The state of this result document will then be undefined, but the transformation can ignore the failure and continue to produce other result documents, for example by wrapping the xsl:result-document instruction in an xsl:try instruction that catches the relevant error.

[ERR XTDE3530] It is a dynamic error if an xsl:try instruction is unable to recover the state of a final result tree because recovery has been disabled by use of the attribute rollback-output="no".

For example, consider the following:

<xsl:result-document href="out.xml">     
  <xsl:try rollback-output="no">
    <xsl:source-document streamable="yes" href="in.xml">
      <xsl:copy-of select="."/>
    </xsl:source-document>
    <xsl:catch errors="*">
       <error code="{$err:code}" message="{$err:description}" file="in.xml"/>
    </xsl:catch>
  </xsl:try>
</xsl:result-document>

The most likely failure to occur here is a failure to read the streamed input file in.xml. In the common case where this failure is detected immediately, for example if the file does not exist or the network connection is down, no output will have been written to the result document, and the attempt to catch the error is likely to be successful. If however a failure is detected after several megabytes of data have been copied to out.xml, for example an XML well-formedness error in the input file, or a network failure that occurs while reading the file, recovery of the output file may be impossible. In this situation the xsl:result-document instruction will fail with a dynamic error. It is possible to catch this error, but the state of the file out.xml will be unpredictable.

Note that adding an xsl:try instruction as a child of xsl:source-document does not help. Any error reading the input file (such as a well-formedness error) is an error in the xsl:source-document instruction and can only be caught at that level.

When rollback-output="no" is specified, it is still possible to ensure recovery of errors happens predictably by evaluating the potentially-failing code in temporary output state: typically, within an xsl:variable. In effect the variable acts as an explicit buffer for temporary results, which is only copied to the final output if evaluation succeeds.

Note:

An application might wish to ensure that when a fatal error occurs while reading an input stream, data written to persistent storage up to the point of failure is available after the transformation terminates. Setting rollback-output="no" does not guarantee this, but a processor might choose to interpret this as the intent.

Changing the attribute to rollback-output="yes" makes the stylesheet more robust and able to handle error conditions predictably, but the cost may be substantial; for example it may be necessary to buffer the whole of the result document in memory.

8.3.2 Try/Catch Examples

Example: Catching a Divide-by-Zero Error

The following example divides an employee’s salary by the number of years they have served, catching the divide-by-zero error if the latter is zero.

<xsl:try select="salary div length-of-service">
  <xsl:catch errors="err:FOAR0001" select="()"/>
</xsl:try>

 

Example: Catching an Error during Result-tree Validation

The following example generates a result tree and performs schema validation, outputting a warning message and serializing the invalid tree if validation fails.

<xsl:result-document href="out.xml">
  <xsl:variable name="result">
    <xsl:call-template name="construct-output"/>
  </xsl:variable>
  <xsl:try>
    <xsl:copy-of select="$result" validation="strict"/>
    <xsl:catch>
      <xsl:message>Warning: validation of result document failed:
          Error code: <xsl:value-of select="$err:code"/>
          Reason: <xsl:value-of select="$err:description"/>
      </xsl:message>
      <xsl:sequence select="$result"/>
    </xsl:catch>
  </xsl:try>
</xsl:result-document>

The reason that the result tree is constructed in a variable in this example is so that the unvalidated tree is available to be used within the xsl:catch element. An alternative approach would be to repeat the logic for constructing the tree:

<xsl:try>
  <xsl:result-document href="out.xml" validation="strict">  
    <xsl:call-template name="construct-output"/>
  </xsl:result-document>
  <xsl:catch>
    <xsl:message>Warning: validation of result document failed:
          Error code: <xsl:value-of select="$err:code"/>
          Reason: <xsl:value-of select="$err:description"/>
    </xsl:message>
    <xsl:call-template name="construct-output"/>
  </xsl:catch>
</xsl:try>

8.4 Conditional Content Construction

The facilities described in this section are designed to make it easier to generate result trees conditionally depending on what is found in the input, without violating the rules for streamability. These facilities are available whether or not streaming is in use, but they are introduced to the language specifically to make streaming easier.

The facilities are introduced first by example:

Example: Generating a Wrapper Element for a non-Empty Sequence

The following example generates an events element if and only if there are one or more event elements. The code could be written like this:

<xsl:if test="exists(event)">
  <events>
    <xsl:copy-of select="event"/>
  </events>
</xsl:if>

However, the above code would not be guaranteed-streamable, because it processes the child event elements more than once. To make it streamable, it can be rewritten as:

<xsl:where-populated>
  <events>
    <xsl:copy-of select="event"/>
  </events>
</xsl:where-populated>

The effect of the xsl:where-populated instruction, as explained later, is to avoid outputting the events element if it would have no children. A streaming implementation will typically hold the start tag of the events element in a buffer, to be sent to the output destination only if and when a child node is generated.

 

Example: Generating a Header and Footer only if there is Content

The following example generates an h3 element and a summary paragraph only if a list of items is non-empty. The code could be written like this:

<xsl:if test="exists(item-for-sale)">
  <h1>Items for Sale</h1>
</xsl:if>  
<xsl:apply-templates select="item-for-sale"/>
<xsl:if test="exists(item-for-sale)">
  <p>Total value: {accumulator-before('total-value')}</p>
</xsl:if>

However, the above code would not be guaranteed-streamable, because it processes the child item-for-sale elements more than once. To make it streamable, it can be rewritten as:

<xsl:sequence>
  <xsl:on-non-empty>
    <h1>Items for Sale</h1>
  </xsl:on-non-empty>  
  <xsl:apply-templates select="item-for-sale"/>
  <xsl:on-non-empty>
    <p>Total value: {accumulator-before('total-value')}</p>
  </xsl:on-non-empty>  
</xsl:sequence>

The effect of the xsl:on-non-empty instruction, as explained later, is to output the enclosed content only if the containing sequence constructor also generates “ordinary” content, that is, if there is content generated by instructions other than xsl:on-empty and xsl:on-non-empty instructions.

 

Example: Generating Substitute Text when there is no Content

The following example generates a summary paragraph only if a list of items is empty. The code could be written like this:

<xsl:apply-templates select="item-for-sale"/>
<xsl:if test="empty(item-for-sale)">
  <p>There are no items for sale.</p>
</xsl:if>

However, the above code would not be guaranteed-streamable, because it processes the child item-for-sale elements more than once (the fact that the list is empty is irrelevant, because streamability is determined statically). To make the code streamable, it can be rewritten as:

<xsl:sequence>
  <xsl:apply-templates select="item-for-sale"/>
  <xsl:on-empty>
    <p>There are no items for sale.</p>
  </xsl:on-empty>
</xsl:sequence>

The effect of the xsl:on-empty instruction, as explained later, is to output the enclosed content only if the containing sequence constructor generates no “ordinary” content, that is, if there is no content generated by instructions other than xsl:on-empty and xsl:on-non-empty instructions.

Note:

In some cases, similar effects can be achieved by using the has-childrenFO30 function, which tests whether an element has child nodes without consuming the children. However, use of has-childrenFO30 has the drawback that the function is unselective: it cannot be used to test whether there are any children of relevance to the application. In particular, it returns true if an element contains comments or whitespace text nodes that the application might consider to be insignificant.

Note:

There are no special streamability rules for the three instructions xsl:where-populated, xsl:on-empty, or xsl:on-non-empty. The general streamability rules apply. In many cases the xsl:on-empty and xsl:on-non-empty instructions will generate content that does not depend on the source document, and they will therefore be motionless, but this is not required.

8.4.1 The xsl:where-populated instruction

<!-- Category: instruction -->
<xsl:where-populated>
  <!-- Content: sequence-constructor -->
</xsl:where-populated>

The xsl:where-populated instruction encloses a sequence constructor. The result of the instruction is established as follows:

  1. The sequence constructor is evaluated in the usual way (taking into account any xsl:on-empty and xsl:on-non-empty instructions) to produce a result $R.

  2. The result of the instruction is the value of the expression $R[not(deemed-empty(.))] where the function deemed-empty($item as item()) returns true if and only if $item is one of the following:

    • A document or element node that has no children.

      Note:

      If an element has attributes or namespaces, these do not prevent the element being deemed empty.

      If a document or element node has children, the node is not deemed empty, even if the children are empty. For example, a document node created using an xsl:variable instruction in the form <xsl:variable name="temp"><a/></xsl:variable> is not deemed empty, even though the contained <a/> element is empty.

    • A node, other than a document or element node, whose string value is zero-length.

      Note:

      A whitespace-only text node is not deemed empty.

    • An atomic value such that the result of casting the atomic value to a string is zero-length.

      Note:

      This can happen only when the atomic value is of type xs:string, xs:anyURI, xs:untypedAtomic, xs:hexBinary, or xs:base64Binary.

    • A map whose size (number of key/value pairs) is zero.

    • An array (see 27.7.1 Arrays) where the result of flattening the array using the array:flattenFO31 function is either an empty sequence, or a sequence in which every item is deemed empty (applying these rules recursively).

Example: Generating an HTML list

The following example generates an HTML unnumbered list, if and only if the list is non-empty. Note that the presence of the class attribute does not make the list non-empty. The code is written to be streamable.

<xsl:where-populated>
  <ul class="my-list">
    <xsl:for-each select="source-item">
       <li><xsl:value-of select="."/></li>
    </xsl:for-each>
  </ul>
</xsl:where-populated>

8.4.2 The xsl:on-empty instruction

<!-- Category: instruction -->
<xsl:on-empty
  select? = expression >
  <!-- Content: sequence-constructor -->
</xsl:on-empty>

The xsl:on-empty instruction has the same content model as xsl:sequence, and when it is evaluated, the same rules apply. In particular, the select attribute and the contained sequence constructor are mutually exclusive [see ERR XTSE3185].

When an xsl:on-empty instruction appears in a sequence constructor, then:

  1. It must be the only xsl:on-empty instruction in the sequence constructor, and

  2. It must not be followed in the sequence constructor by any other instruction, other than xsl:fallback, or by a significant text node (that is, a text node that has not been discarded under the provisions of 4.3 Stripping Whitespace from the Stylesheet), or by a literal result element. It may, however, be followed by non-instructions such as xsl:catch where appropriate.

[Definition: An item is vacuous if it is one of the following: a zero-length text node; a document node with no children; an atomic value which, on casting to xs:string, produces a zero-length string; or (when XPath 3.1 is supported) an array which on flattening using the array:flattenFO31 function produces either an empty sequence or a sequence consisting entirely of vacuous items.]

An xsl:on-empty instruction is triggered only if every preceding sibling instruction, text node, and literal result element in the same sequence constructor returns either an empty sequence, or a sequence consisting entirely of vacuous items.

If an xsl:on-empty instruction is triggered, then the result of the containing sequence constructor is the result of the xsl:on-empty instruction.

Note:

This means that the (vacuous) results produced by other instructions in the sequence constructor are discarded. This is relevant mainly when the result of the sequence constructor is used for something other than constructing a node: for example if it forms the result of a function, or the value of a variable, and the function or variable specifies a required type.

When streaming, it may be necessary to buffer vacuous items in the result sequence until it is known whether the result will contain items that are non-vacuous. In many common situations, however — in particular, when the sequence constructor is being used to create the content of a node — vacuous items can be discarded immediately because they do not affect the content of the node being constructed.

Note:

In nearly all cases, the rules for xsl:on-empty are aligned with the rules for constructing complex content. If the sequence constructor within a literal result element or an xsl:element instruction includes an xsl:on-empty instruction, then the content of the element will be the value delivered by the xsl:on-empty instruction if and only if the content would otherwise be empty.

There is one minor exception to this rule: if the sequence constructor delivers multiple zero-length strings, then in the absence of the xsl:on-empty instruction the new element would contain whitespace, made up of the separators between these zero-length strings; but xsl:on-empty takes no account of these separators.

Note:

Attribute and namespace nodes created by the sequence constructor are significant; the xsl:on-empty instruction will not be triggered if such nodes are present. If this is not the desired effect, it is possible to partition the sequence constructor to change the scope of xsl:on-empty, for example:

<ol>
  <xsl:attribute name="class" select="numbered-list"/>
  <xsl:sequence>
    <xsl:value-of select="xyz"/>
    <xsl:on-empty select="'The list is empty'"/>
  </xsl:sequence>
</ol>

Note:

Where the sequence constructor is a child of an instruction with an [xsl:]use-attribute-sets attribute, any attribute nodes created by expanding the referenced attribute set(s) are not part of the result of the sequence constructor and therefore play no role in determining whether an xsl:on-empty or xsl:on-non-empty instruction is triggered. Equally, when the sequence constructor is a child of a literal result element, attribute nodes generated by expanding the attributes of the literal result element are not taken into account.

Note:

If xsl:on-empty is the only instruction in a sequence constructor then it is always evaluated.

If xsl:on-empty and xsl:on-non-empty appear in the same sequence constructor, then the rules ensure that only one of them will be evaluated.

8.4.3 The xsl:on-non-empty instruction

<!-- Category: instruction -->
<xsl:on-non-empty
  select? = expression >
  <!-- Content: sequence-constructor -->
</xsl:on-non-empty>

The xsl:on-non-empty instruction has the same content model as xsl:sequence, and when it is evaluated, the same rules apply. In particular, the select attribute and the contained sequence constructor are mutually exclusive [see ERR XTSE3185].

An xsl:on-non-empty instruction is evaluated only if there is at least one sibling node in the same sequence constructor, excluding xsl:on-empty and xsl:on-non-empty instructions, whose evaluation yields a sequence containing an item that is not vacuous. If this condition applies, then all xsl:on-non-empty instructions in the containing sequence constructor are evaluated, and their results are included in the result of the containing sequence constructor in their proper positions.

Note:

The xsl:on-non-empty instruction is typically used to generate headers or footers appearing before or after a list of items, where the header or footer is to be omitted if there are no items in the list.

Note:

Unlike xsl:on-empty, the xsl:on-non-empty instruction can appear anywhere in a sequence constructor, and can appear more than once.

8.4.4 Evaluating xsl:on-empty and xsl:on-non-empty Instructions

The following non-normative algorithm explains one possible strategy for streamed evaluation of a sequence constructor containing xsl:on-empty and/or xsl:on-non-empty instructions.

The algorithm makes use of the following mutable variables:

  • L : a list of instructions awaiting evaluation. Initially empty.

  • R : a list of items to act as the result of the evaluation. Initially empty.

  • F : a boolean flag, initially false, to indicate whether any non-vacuous items have been written to R by ordinary instructions. The term ordinary instruction means any node in the sequence constructor other than an xsl:on-empty or xsl:on-non-empty instruction.

The algorithm is as follows:

  1. The nodes in the sequence constructor are evaluated in document order.

  2. When an xsl:on-non-empty instruction is encountered, then:

    1. If F is true, the instruction is evaluated and the result is appended to R.

    2. Otherwise, the instruction is appended to L.

  3. When an ordinary instruction is evaluated:

    1. The results of the evaluation are appended to R, in order.

    2. When a non-vacuous item is about to be appended to R, and F is false, then before appending the item to R, the following actions are taken:

      1. Any xsl:on-non-empty instructions in L are evaluated, in order, and their results are appended to R.

      2. F is set to true.

  4. When an xsl:on-empty instruction is encountered, then:

    1. If F is true, the instruction is ignored.

    2. Otherwise, the existing contents of R are discarded, the instruction is evaluated, and its results are appended to R.

      Note:

      The need to discard items from R arises only when all the items in R are vacuous. Streaming implementations may therefore need a limited amount of buffering to retain insignificant items until it is known whether they will be needed. However, in many common cases an optimized implementation will be able to discard vacuous items such as empty text nodes immediately, because when a node is being constructed using the rules in 5.7.1 Constructing Complex Content or 5.7.2 Constructing Simple Content, such items have no effect on the final outcome.

      Otherwise, the instruction is evaluated and its results are appended to R.

  5. The result of the sequence constructor is the list of items in R.

8.4.5 A More Complex Example

This example shows how the three instructions xsl:where-populated, xsl:on-empty, and xsl:on-non-empty may be combined.

Example: Generating a Table only if there is Content

The following example generates a table containing the names and ages of a set of students; if there are no students, it substitutes a paragraph explaining this.

<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>

Explanation:

  • The xsl:where-populated around the table element ensures that if there is no thead and no tbody, then there will be no table.

  • The xsl:on-non-empty surrounding the thead element ensures that the thead element is not output unless the tbody element is output.

  • The xsl:where-populated around the tbody element ensures that the tbody element is not output unless there is at least one table row (tr).

  • The xsl:on-empty around the p element ensures that if no table is output, then the paragraph There are no students is output instead.