More XSLT
R. Alexander Milowski
milowski@sims.berkeley.edu
School of Information Management and Systems
#1
Contents
More XPath Functions
Variables, parameters, and result tree fragments.
Sorting
Conditionals and their use.
#2
String Manipulation
You can manipulate values (strings) just like in programming languages.
substring-before("1999/04/01","/") returns "1999"
substring-after("1999/04/01","/") returns "04/01"
contains("big geek","geek") returns true.
The can all use XPath expressions too:
substring-before(@date,"/")
substring-after(book/pubdate,$delimiter)
#3
Strings and xsl:value-of
These are the same:
<xsl:value-of select="name"/> <xsl:value-of select="string(name)"/>
Implicit conversions of node sets to strings is via string():
The first node in the set is used.
The "text" descendants are the value.
#4
Numbers
All numbers in XSLT are floating point.
Conversion to numbers can be implicit.
You can explicitly convert to a number via: number(expr).
Summing numbers:
<xsl:template match="items"> <p>Total: <xsl:value-of select="sum(item/cost)"/></p> </xsl:template>
#5
Number Formatting
format-number() function can format a number.
A picture string is used: #.00
The full specification is from the Java class java.text.DecimalFormat or in the book.
Example
format-number("5.5","#.00") → 5.50
#6
Identifiers
Sometimes you need unique identifiers in the output.
generate-id(node) will return the same unique identifier for a given node.
generate-id() just returns a value--your schema says if it is valid where you use it.
Example:
<xsl:template match="annotation"> <xsl:copy> <xsl:attribute name="id"><xsl:value-of select="generate-id(.)"/></xsl:attribute> </xsl:copy> </xsl:template>
#7
Language
Matching language attributes can also be accomplished via a lang() function.
Example:
<terms> <term xml:lang="en"></term> <term xml:lang="fr"></term> <term xml:lang="pl"></term> </terms>
to find the Polish language term:
<xsl:template match="term[lang('pl')]"> Polish: <xsl:apply-templates/> </xsl:template>
#8
Multiple Input Documents
You can create aggregates with document()
This includes additional documents as inputs to the transformation.
The document() function can have one or two arguments (see next slide).
Example:
<xsl:template match="include"> <xsl:apply-templates select="document(@href)"/> </xsl:template>
#9
Base URI & document()
If the first argument is a node set:
Each node in the set is processed by an implicit call to document().
The first argument is the node's value via string()
The second argument is the node (so relative URI values work).
If the URI value is relative, the base URI is the node set in context:
If the there is only one argument and it is a node set, then the base URI is that of the node being processed.
If the there is only one argument and it is a not a node set, then the base URI is that of the stylesheet.
If the there are two arguments, the base URI of the second is used.
An example: docfunc.xsl example.xml one.xml two.xml
#10
Aggregation Example
I've got a master document:
<talk-master><title>My Talk</title> <slide-group href="group-1.xml"/> <slide-group href="group-2.xml"/> </talk-master>
And I can then merge these documents with:
<xsl:template match="slide-group"> <xsl:apply-templates select="document(@href)/slide-group/node()"/> </xsl:template>
Here's that stylesheet and example: merge-slides.xsl talk-master.xml group-1.xml group-2.xml
#11
Built-in Template Rule
There are two built-in template rules:
For parents (elements and documents):
<xsl:template match="*|/"> <xsl:apply-templates/> </xsl:template>
For text and attributes:
<xsl:template match="text()|@*"> <xsl:value-of select="."/> </xsl:template>
For processing instructions and comments
<xsl:template match="processing-instruction()|comment()"/>
These rules, used alone, give you just the "text" along the 'child' axis.
#12
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)
#13
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.
#14
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.
#15
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]"/>
#16
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.
#17
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
#18
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.
#19
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>
#20
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>
#21
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>
#22
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).
#23
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>
#24
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>
#25
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>
#26
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>
#27
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'.
#28
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>
#29
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>
#30
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.
#31
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>
#32
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.