Predicates & Conditionals in XPath & XSLT
R. Alexander Milowski
School of Information Management and Systems
milowski at sims.berkeley.edu
#1
Overview
We'll cover:
predicates in more detail
positions
variables, parameters, and result tree fragments
sorting and iteration
conditional actions
#2
Predicates Semantics
Predicates result in an expression value that is converted to a boolean.
If the value is a number, then the position is checked against that number.
These are equivalent:
slide[2] slide[position()=2]
Otherwise, the boolean() function is applied to convert its value to a boolean:
A node set is true if it is non-empty.
A string is true if it has a non-zero length.
Boolean expressions (and, or, not ) are already boolean values.
#3
Predicates - Operators
Predicates can be separate by boolean operators: 'or' , 'and'
a[@name or @href] word[syn and @link]
Negation is a function
a[not(@name)]
Values can be compared by: =, !=, <, >, <=, >=
a[@name='termlink'] word[@type!='noun' and position()>1]
#4
Multiple Predicates
Predicates can be specified separately:
a[@name][@href] slide[1][notes]
Each predicate is filtered by the next.
They have equivalent forms as 'and' predicates:
a[@name and @href] slide[position()=1 and notes]
Separate predicates may be easier to write.
#5
Predicates and Steps
Predicates don't have to be at the end:
computer[price]/specs slide[citation]/cite[@ref]
The "filter" is applied at each step.
Keep in mind that predicates have additional cost and templates may be faster:
<xsl:template match="slide[citation]"> <xsl:apply-templates select="cite[@ref]"/> </xsl:template>
#6
Predicates - Positions
Positions are relative to what you selected in the step.
They are not necessarily the sibling the position in the input.
For example, given:
<top> <a/><b/><a/><b/> </top>
the expressions:
top/a[1] top/a[2] top/b[1] top/b[2]
select all the element children of 'top'. These do not:
top/a[1] top/b[2] top/a[3] top/b[4]
#7
Result Tree Fragments
The results of applying templates is a "result tree".
These aren't node sets.
The value of a result tree is like calling string().
Which becomes important for variables... (see following slides)
#8
Variables
Variables allow you to save values for later use.
They have a static scope of either the transformation or a template.
That is, they can't be re-declared in their scope.
But other templates can declare them again.
#9
Variables - Syntax
You declare a variable using xsl:variable
<xsl:variable name="var-name-1">value</xsl:variable> <xsl:variable name="var-name-2" select="expression"/>
They can be used in attribute value templates and XPath expressions:
<value name="{$value-name}"/> <xsl:apply-templates select="$somewhere/items"/>
If a variable's value is a node, you can select against it with step expressions.
Variable names follow the same rules as elements:
They can be QNames (qualified by a namespace).
Prefixes resolve against the stylesheet document's namespace declarations.
#10
Variables - Saving the Context
This doesn't work:
<xsl:apply-templates select="citation[@id=./@ref]"/>
because the context node changes with the step expression.
But you can save the context and use it later:
<xsl:variable name="current" select="."/> <xsl:apply-templates select="citation[@id=$current/@ref]"/>
Or just save the value:
<xsl:variable name="ref" select="@ref"/> <xsl:apply-templates select="citation[@id=$ref]"/>
#11
Variables - Result Tree Fragments vs. Node Sets
These are different:
<xsl:variable name="stuff" select="person"/>
<xsl:variable name="stuff"><xsl:apply-templates select="person"/></xsl:variable>
The first is a node set and can be selected against:
<xsl:apply-templates select="$stuff/name"/>
The second can't be manipulated.
Which means you can't use the results of template transforms in the same stylesheet.
...but there are extension functions which let you do so and XSLT 2.0 will as well.
#12
Variables - Top Level Variables
Sometimes it is useful to have "global" variables.
They get processed once against the input document.
Example:
<xsl:transform xmlns:xsl=''> <xsl:variable name="citations" select="//citations"/>
and then use it later:
<xsl:apply-templates select="$citations[@id=$ref]"/>
Here's that example: globals.xsl citations.xml
#13
Parameters
Parameters are like variables.
But they can be set from the outside.
They can also have defaults.
The usage syntax is the same as variables.
#14
Parameters to Stylesheets
The xsl:param element is used to declare them:
<xsl:param name="param-name-1"/> <xsl:param name="param-name-2">default value</xsl:param>
These must occur at the top level.
A parameter can be set by the using application but the stylesheet can define defaults by:
The children of xsl:param.
A 'select' attribute.
So they typically have simple values.
Example:
<xsl:param name="background-color">white</xsl:param>
#15
Parameters to Templates
Templates can have parameters just like stylesheets.
Their names can't conflict with variables.
Example:
<xsl:template match="element"> <xsl:param name="namespace"/> <xsl:element name="{@name}" namespace="{$namespace}"/> </xsl:template>
Bad:
<xsl:template match="element"> <xsl:param name="namespace"/> <xsl:variable name="namespace" select="namespace()"/> <xsl:element name="{@name}" namespace="{$namespace}"/> </xsl:template>
#16
Passing Parameters
A parameter value must be passed by the immediately preceding template.
This is done by an xsl:with-param element as a child of xsl:apply-templates:
<xsl:apply-templates> <xsl:with-param name="name">value</xsl:with-param> </xsl:apply-templates>
The bindings are only in effect for one template application.
The value can also be specified by a 'select' attribute.
The previous slide's example's call:
<xsl:apply-templates select="element"> <xsl:with-param name="namespace">urn:publicid:IDN+...</xsl:with-param> </xsl:apply-templates>
#17
Iterating with for-each
You can iterate expressions with xsl:for-each.
Syntax:
<xsl:for-each select="item"> <p><xsl:value-of select="."/></p> </xsl:for-each>
The context node is set to each node in the result set.
Useful for iterating node sets where you don't want to create an additional template (e.g. you don't want to duplicate variables).
#18
Setting the Context with for-each
xsl:for-each can be used to set the context node.
Example:
<xsl:for-each select="preceding-sibling::slide[1]"> <!-- the context is now the previous slide --> </xsl:for-each>
#19
Sorting
You can re-order elements with xsl:sort.
This only applies to xsl:apply-templates and xsl:for-each.
You place an xsl:sort element as the first child of xsl:apply-templates or xsl:for-each.
Example:
<xsl:apply-templates> <xsl:sort select="name"/> </xsl:apply-templates>
#20
Simple Sort Example
Given the document:
<vendors> <vendor><name>Dell</name>...</vendor> <vendor><name>Apple</name>...</vendor> <vendor><name>HP</name>...</vendor> </vendors>
This would sort the vendor elements:
<xsl:template match="vendors"> <xsl:copy> <xsl:apply-templates select="vendor"> <xsl:sort select="name"/> </xsl:apply-templates> </xsl:copy> </xsl:template> <xsl:template match="vendor"> <xsl:copy-of select="."/> </xsl:template>
#21
Multiple Sorts
You can have more than one xsl:sort element.
They provide addition sorts for when all the previous sorts are equal.
Example:
<xsl:template match="employees"> <employees> <xsl:apply-templates select="employee"> <xsl:sort select="name/last"/> <xsl:sort select="name/first"/> </xsl:apply-templates> </employees> </xsl:template>
#22
Controlling Sorting
There are attributes on xsl:sort that control the sorting:
order='ascending|descending'
This controls which way the sort is returned and defaults to 'ascending'.
case-order='upper-first|lower-first'
Defines which case is first and the default is language dependent.
lang='' controls the language of the sort (e.g. French).
data-type='text|number' controls when the data is treated as text or a number.
This can also be a QName to specify processor-specific sorting.
The default is 'text'.
#23
xsl:if
You can specifiy a condition for an action with xsl:if
The condition is an XPath contained in a 'test' attribute.
There is no "else" part.
Example:
<xsl:template match="namelist/name"> <xsl:apply-templates/> <xsl:if test="preceding-sibling::*">, </xsl:if> </xsl:template>
#24
xsl:choose
You can encode a switch statement with xsl:choose
Each case is represented with an xsl:when element.
xsl:when works just like xsl:if
The default case can be specified by xsl:otherwise
Syntax:
<xsl:choose> <xsl:when test="...">...</xsl:when> <xsl:when test="...">...</xsl:when> <xsl:otherwise>...</xsl:otherwise> </xsl:choose>
#25
xsl:choose - semantics
Each case is tested in the order they appear.
An implementation could optimize this.
But in case of conflicts, the first is used.
The whole xsl:choose element is replaced by the actions that were performed.
#26
xsl:choose - example
This example tests an attribute for a specified set of values:
<xsl:choose> <xsl:when test="@status='draft'> <p>This document is in draft status.</p> </xsl:when> <xsl:when test="@status='final-draft'> <p>This document is is ready for final review.</p> </xsl:when> <xsl:when test="@status='published'> <p>This document has been published as <xsl:value-of select="{@pub-id}"/></p> </xsl:when> <xsl:otherwise> <p>This document has no official status.</p> </xsl:otherwise> </xsl:choose>
#27
Conditionals vs. Templates
Conditionals can take the place of templates.
But then your stylesheets are less flexible.
Conditionals exclude overriding:
When importing templates, they can't be overriden.
Other templates have to duplicate the same work.
When the document structure changes, they will typically fail.
But they are nice for when you don't want the context, variables, and parameters to change.