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.
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.
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>
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.
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>
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 error
FO30 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:
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 error FO30 function was called with one
argument).
|
err:value | item()* | Value associated with the error. For an error raised by
calling the error FO30 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:
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
error
FO30 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
.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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>
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>
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:
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.
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.
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-children
FO30 function, which tests whether an element
has child nodes without consuming the children. However, use of
has-children
FO30 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.
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:
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.
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:flatten
FO31 function is
either an empty sequence, or a sequence in which every item is deemed empty
(applying these rules recursively).
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>
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:
It must be the only xsl:on-empty
instruction in the
sequence constructor, and
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:flatten
FO31 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.
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.
xsl:on-empty
and xsl:on-non-empty
InstructionsThe 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:
The nodes in the sequence constructor are evaluated in document order.
When an xsl:on-non-empty
instruction is encountered,
then:
If F is true, the instruction is evaluated and the result is appended to R.
Otherwise, the instruction is appended to L.
When an ordinary instruction is evaluated:
The results of the evaluation are appended to R, in order.
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:
Any xsl:on-non-empty
instructions in
L are evaluated, in order, and their results are
appended to R.
F is set to true.
When an xsl:on-empty
instruction is encountered, then:
If F is true, the instruction is ignored.
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.
The result of the sequence constructor is the list of items in R.
This example shows how the three instructions
xsl:where-populated
, xsl:on-empty
, and
xsl:on-non-empty
may be combined.
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.