Advanced Topics in XSLT
R. Alexander Milowski
School of Information Management and Systems
milowski at sims.berkeley.edu
#1
Modular Stylesheets
Often you want to break up a complicated stylesheet.
Also, you might want to provide transformations as a library.
But you might want to:
Include whole stylesheets as if they thier definitions were inlined.
Override declarations and templates in what you include.
So there are two facilities for modular stylesheets.
#2
xsl:include
The xsl:include element allows you to pull in an external set of declarations.
Syntax:
<xsl:include href="people.xsl"/>
It can occur anywhere at the top level.
The URI is relative to the stylesheet.
The conflict resolution for included templates is the same as if you cut-and-pasted the definitions.
#3
xsl:import
xsl:import is like xsl:include:
<xsl:import href="basic-style.xsl"/>
But it must occur before all other top-level elements (except other xsl:import elements).
Crutial difference:
Imported templates have lower "precedence".
The highest precedence template always wins
#4
xsl:apply-imports
You can apply the imported stylesheet's templates.
<xsl:apply-imports/> will apply over-ridden templates.
Example: Wrap an existing template in a 'div' element:
<xsl:template match="person"> <div class="person"> <xsl:apply-imports/> </div> </xsl:template>
#5
Importing Example
We have a base stylesheet that just outputs a person's name.
We now want e-mail addresses but we don't want to change the original stylesheet.
So we import our base stylesheet people-base.xsl
In the importing stylesheet people-extended.xsl we have:
An xsl:import at the top.
A new template for the 'person' that overrides the old.
Example input document: people-1.xml
#6
Named Templates
Common actions can be separted as a "named template".
The syntax is very similar:
<xsl:template name="internal-style"> <style type="text/css"> ... </style> </xsl:template>
The context node remains the same when it is used:
<xsl:template name="make-attribute"> <xsl:attribute name="altid/@type"> <xsl:apply-templates/> </xsl:attribute> </xsl:template>
#7
Calling Named Templates
The action xsl:call-template is used to call a named template.
Example:
<xsl:template match="prolog"> <xsl:call-template name="internal-style"/> </xsl:template>
Keep in mind that the name is a QName--which will be important when we use multiple stylesheet modules.
#8
Named Template Parameters
You can have parameters just like regular templates.
xsl:param and xsl:with-param work the same.
Example:
<xsl:template name="frog"> <xsl:param name="color"/> <frog color="{$color}"/> </xsl:template> <xsl:template name="animal[@type='frog']"> <xsl:call-template name="frog"> <xsl:with-param name="color" select="@color"/> </xsl:call-template> </xsl:template>
#9
Keys
Keys provide a way to cross-reference (index) elements and attributes within a document.
A key is a set with elements that have three parts:
The node that has the key.
The name of the key.
A string value for the key.
A key declaration matches these parts.
<xsl:key name='QName' match='pattern' use='expression' />
#10
Key Declarations
A key declaration exists only in the stylesheet.
Syntax
<xsl:key name='QName' match='pattern' use='expression' />
Semantics:
name - the name of the key
match - node to which the key applies
use - the value to use for the key
#11
Using Keys
Keys are traversed via the key() function.
Syntax:
<xsl:apply-templates select="key('name','value')"/> <xsl:apply-templatee select="key('name',@ref)"/>
The results are all nodes in the key whose value matches the first argument.
#12
Key Example - Cross References
Example content:
<prototype name="key" return-type="node-set"> <arg type="string"/> <arg type="object"/> </prototype> <function>key</function>
Example Use
<xsl:key name='func' match='prototype' use='@name'/> <xsl:template match="function"> <p>The return type is <xsl:value-of select="key('func',.)/@return-type"/> </p> </xsl:template>
Which would give you:
<p>The return type is node-set</p>
#13
Grouping via Keys
Keys can be used to group elements.
For example, grouping elements with like names (e.g. computers by vendor).
For reference, see http://www.jenitennison.com/xslt/grouping/
#14
Grouping Strategy
Define a key for the value you want to group by.
Iterate each unique value of the key.
Access the key with the value from (2) to get each grouping.
#15
Finding Unique Key Values
Find the first element of every key for the representative value.
By generated identifiers:
computer[generate-id()=generate-id(key('computer-by-vendor',vendor)[1])]
This works because the same node must return the same value from generate-id().
By node identity:
computer[count(.|key('computer-by-vendor',vendor)[1]) = 1]
This works because a node set cannot contain the same node twice.
#16
Grouping Example
This example uses both keys and modes.
Every 'computer' element in my inventory has a 'vendor' by which I want to group:
Define the key:
<xsl:key name="vendors" match="computer" use="vendor"/>
Find the unique key values:
<xsl:apply-templates select="computer[count(.|key('vendors',vendor)[1])=1]">
Process the unique value:
<xsl:template match="computer"> <vendor> <xsl:apply-templates select="vendor"/> <!-- Now we access the full set of elements that match the key value. These will be copied across in a special "identity" mode. --> <xsl:apply-templates select="key('vendors',vendor)" mode="computer-copy"/> </vendor> </xsl:template>
Full Example: inventory.xml group-by.xsl
#17
Keys & Processing Costs
The XML parser doesn't know about the key.
So it doesn't exist till the XSLT processor gets to the document.
This means the XSLT processor has to make one pass through the document to provide the key.
Often this is only done when the key is first accessed.
#18
Numbering with XSLT
You can let XSLT count occurrences of elements.
This lets you do things like hierarchical numbering.
An example:
<xsl:template match="section/title"> <title>Section <xsl:number count="section" level="multiple" format="1.1. "/> <xsl:apply-templates/> </title> </xsl:template>x
Full Example: number.xsl sections.xml
Read chapter 9 in Learning XSLT for more information.
#19
excluding namespace prefixes
You can control the namespace declarations in the output.
You can add the attribute 'exclude-result-prefixes' to xsl:transform, xsl:stylesheet, or any literal element.
The value is a list of prefixes that are associated with the namespaces you don't want.
If the processor doesn't need them, it will exclude them.
Sometimes this is an issue for "dumb" XML parsers.
#20
Exclusion Example
You typically want this for source namespaces:
<xsl:transform xmlns:xsl='http://www.w3.org/1999/XSL/Transform' exclude-result-prefixes='m' xmlns:m='http://www.w3.org/1999/MathML" > <xsl:template match="m:math"> <p>Math is not supported</p> </xsl:template> </xsl:transform>
#21
Namespaces Aliases
Sometimes you want to use a namespace in the stylesheet but:
The processor has semantics for the namespace you want to use.
You're creating an XSLT stylesheet within an XSLT stylesheet.
xsl:namespace-alias will allow you to defined an alias for the stylesheet:
<xsl:namespace-alias stylesheet-prefix='from' result-prefix='to'/>
#22
Namespaces Aliases - Example
Given:
<aliases> <para-alias>name</para-alias> <para-alias>person-name</para-alias> <para-alias>para</para-alias> </aliases>
the following generates a specific stylesheet with a template for each alias:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:axsl="http://www.w3.org/1999/XSL/TransformAlias"> <xsl:namespace-alias stylesheet-prefix="axsl" result-prefix="xsl"/> <xsl:template match="/"> <axsl:stylesheet> <xsl:apply-templates/> </axsl:stylesheet> </xsl:template> <xsl:template match="para-alias"> <axsl:template match="{.}"> <p><axsl:apply-templates/></p> </axsl:template> </xsl:template> </xsl:stylesheet>
#23
messages
You can send messages to the using application via xsl:message:
Syntax:
<xsl:message terminate='yes'>Halt and catch fire.</xsl:message>
The 'terminate' attribute controls whether the XSLT processor stops.
'terminate' defaults to 'no'.
The spec says:
"If the terminate attribute has the value yes, then the XSLT processor should terminate processing after sending the message."