XSLT 2.0 and 1.0 Foundations by Dimitre Novatchev
You can't learn XSLT without XPath.
XSLT is built around concept of templates.
XSLT can use one or even more XML as input data. Templates can be viewed as peace of code that must target using XPath some elements of XML input.
Ideally you are responsible to write templates that should cover all input data. You can do this with 1 or unlimited number of templates. Each template may nest another. This relates to inherited hierarchy of XML.
Furthermore you should be aware that there is priority and precedence applied on templates. It determines order of execution and resolve conflicts if two templates match same data.
Sample:
<xsl:template match="persons">
<!-- Render any data related to all persons -->
<!-- Here nesting happens. XSLT processor will search for templates in rest of XSLT document that match XML data from this contextual point/node -->
<xsl:apply-templates/>
</xsl:template>
Following template matches person and renders it inside above <xsl:apply-templates/>
<xsl:template match="person">
<xsl:value-of select="first-name"/>
<xsl:value-of select="last-name"/>
</xsl:template>
Built-in templates
By default XSLT processor will catch and render all data from source XML that hasn't been processed by your templates. Of course this is rarely wanted behavior.
Dimitre Novatchev has written templates that override these built-in template and outputs descriptive warning about missing matches for elements, nodes, attributes which is very helpful.
<!-- Built-in templates: Explicit -->
<xsl:template match="*" mode="#all" priority="-999">
<xsl:message>
Unmatched element <xsl:value-of select="name()"/>
</xsl:message>
<xsl:apply-templates mode="#current"/>
</xsl:template>
<xsl:template match="text()" mode="#all">
<xsl:message>Unmatched text node</xsl:message>
"<xsl:value-of select="string(.)"/>"
</xsl:template>
<xsl:template match="@*" mode="#all">
<xsl:message>
Unmatched attribute <xsl:value-of select="name()"/>
</xsl:message>
<xsl:value-of select="string(.)"/>
</xsl:template>
Overriding templates
If you explicitly don't want to render some data just write empty template:
<xsl:template match="person" />
Pull vs Push stlye
This:
<xsl:apply-templates />
; is push style. We didn't target which explicit templates we want here so they'l get pushed in document order.
When following template gets executed:
<xsl:template match="name|age|profession">
<td><xsl:value-of select="."/></td>
</xsl:template>
; we can't force order in which name, age or profession are rendered. Order is determined by input XML data. To change this use pull style.
For XSLT 1.0 use:
<xsl:apply-templates select="name"/>
<xsl:apply-templates select="age"/>
<xsl:apply-templates select="profession"/>
This way we explicitly match place to render with code that renders it with select attribute.
Variables
They follow naming and expression that you should know from XPath.
<xsl:variable name="personsCount" select="count(person)"/>
<xsl:value-of select="$personsCount"/>
When defined as child of top <xsl:stylesheet> element it is global.
Here is an example of local:
<xsl:variable name="fullName" select="concat(name,' ',age)"/>
<xsl:value-of select="$fullName"/>
Modularity
As in procedural languages you can place your XSLT code in separate files or modules.
You reference these modules using :
- <xsl:include>
- <xsl:import>
Code included with Include has higher importance. It is treated as if it was written in primary stylesheet. If by mistake you INCLUDE same code you'll create conflict. Include instruction may be placed anywhere.
Import on the other hand can't create conflict in primar stylesheet. It's content is always treated with less precedence and priority than one in primary stylesheet. You must write it as the very first child of <xsl:stylesheet>
Precedence is determined by order of IMPORT instructions. Last one wins.
Precedence
When 2 or more templates match same XML content XSLT processor must choose a winner.
This is usually case for complex xslt comprised from multiple import/includes.
In such cases same match in different xslt modules may occur.
One solution is to play with priority attribute of template instruction:
<xsl:template match="name|age|profession">
<td><xsl:value-of select="."/></td>
</xsl:template>
<xsl:template match="name|age|profession">
<td style="color:red">
<xsl:value-of select="."/>
</td>
</xsl:template>
Observe in above sample that both templates match same data. There will be no error and LAST template in document order will be the winner.
Alternatively we could change first template like this:
<xsl:template match="name|age|profession" priority="2">
This way the first template would win.
Same rule goes for variables. Variable from imported module has LESS priority then same one in primary stylesheet.
Data types
<xsl:variable name="salary" select="xs:decimal(0.1)"/>
For-each
Loop is very similar to apply templates.
There is support for sorting:
<xsl:for-each select="person">
<xsl:sort data-type ="text" select="age" order="descending"/>
<p>
<xsl:value-of select="name"/>
<xsl:value-of select="age"/>
<xsl:value-of select="profession"/>
</p>
</xsl:for-each>
Sorting
You can perform sorting on both for-each and apply-templates instructions on same way:
<xsl:apply-templates>
<xsl:sort data-type ="text" select="age" order="descending"/>
</xsl:apply-templates>
Apply-templates vs for-each
Dimitre Novatchev made great example for this:
XML document:
<creatures>
<animal species="dog"/>
<animal species="cat"/>
</creatures>
XSLT document:
<xsl:template match="creatures">
<xsl:apply-templates mode="say"/>
</xsl:template>
<xsl:template match="animal[@species='dog']" mode="say">
Dog says: "Bow" ...
</xsl:template>
<xsl:template match="animal[@species='cat']" mode="say">
Cat says: "Meauu" ...
</xsl:template>
<xsl:template match="text()" mode="say"/>
Foreach is functionally equivalent foreach in C#. Apply template can be viewed as virtual method.
In above example first template resembles abstract class. It creates generic behavior for functionality Say of animals. Than more specialized templates handle specific cases for each animal. We could say it resembles overriding virtual methods.
Observe last template. If all fails it gets executed. Do note use of mode attribute.
Bottom line is to use for-each for simple tasks and apply-templates for complex problems that are easier solved using more than one place for code.
No comments:
Post a Comment