We've seen that when you apply templates to
a node, the XSLT processor tries to find the template that matches that node.
But what happens when there isn't a template that matches a node? For example,
if we applied templates to the <Name> child of the <Channel>
element, as follows, but didn't have a template to match the <Name> element:
<xsl:template
match="Channel">
<h2 class="channel"><xsl:apply-templates
select="Name" /></h2>
<xsl:apply-templates select="Program" />
</xsl:template>
When the XSLT processor can't find a
template to match the node that it's been told to process, it uses a built-in
template. If you find that the result of your
stylesheet includes text you didn't expect, the chances are that it's due to
the built-in templates. Just because there isn't a template for a particular
node that doesn't mean that it's not processed.
For elements, the built-in template is as
follows:
<xsl:template
match="*">
<xsl:apply-templates />
</xsl:template>
This template uses two bits of syntax that
we haven't seen before:
The match attribute of the template takes the value *.
Templates with a match pattern of * match
all elements.
The <xsl:apply-templates> element doesn't have a select
attribute. If you use <xsl:apply-templates> without a select
attribute, the XSLT processor collects all the children of the current node
(which is the node that the template matches) and applies templates to them.
To see the effect of this, take another
look at the part of the node tree containing the <Name> element:
The <Name> element has only one child node, a text node with the value BBC1. When you tell the XSLT processor to apply templates to the <Name> element, it will use the built-in template for elements, and hence
apply templates to the text node.
Now, again, we don't have a template that matches
text nodes in our stylesheet, so the processor uses a built-in template. The
built-in template for text nodes is:
<xsl:template
match="text()">
<xsl:value-of select="." />
</xsl:template>
Again, this template uses a couple of new
bits of syntax:
The match attribute of the <xsl:template> element takes the value text().
Templates with a match pattern of text()
match text nodes.
The select attribute of the <xsl:value-of> element takes the value .. The
path . selects the
context node, so <xsl:value-of select="." /> gives the
value of the context node, in this case the text node.
In combination, these two built-in
templates mean that if you apply templates to an element, but don't have a
template for that element (or any elements it contains), then you'll get the
value of the text held within the element. So applying templates to the <Name> element means that you get the value BBC1 in the result.
If a processor can't find a
template that matches a node, it uses the built-in template for that node type.
In effect, these give the value of the elements to which you apply templates.
There are quite a few places in our
stylesheet where we want to just get the value of an element. Rather than using
<xsl:value-of> to get these values,
we could apply templates to the elements and let the built-in templates do
their work to give us the
element values.
We've already done this with the template
for <Channel> elements, to get the
name of the channel:
<xsl:template
match="Channel">
<h2 class="channel"><xsl:apply-templates
select="Name" /></h2>
<xsl:apply-templates select="Program" />
</xsl:template>
We can also replace the <xsl:value-of> instructions in the template for <Program> elements to get the values of the <Start>, <Series>, and <Description> elements:
<xsl:template
match="Program">
<div>
<p>
<span class="date"><xsl:apply-templates
select="Start" /></span>
<br />
<span class="title"><xsl:apply-templates
select="Series" /></span>
<br />
<xsl:apply-templates select="Description" />
<span
onclick="toggle({Series}Cast);">[Cast]</span>
</p>
<div id="{Series}Cast" style="display:
none;">
<ul class="castlist">
<xsl:apply-templates
select="CastList/CastMember" />
</ul>
</div>
</div>
</xsl:template>
and in the template for <CastMember> elements, to get the values of the <Character> and
<Actor> elements:
<xsl:template
match="CastMember">
<li>
<span class="character">
<xsl:apply-templates select="Character" />
</span>
<span
class="actor">
<xsl:apply-templates select="Actor" />
</span>
</li>
</xsl:template>
Both
the <Character> and <Actor> elements actually contain <Name> elements giving the name of the character or actor. With the
built-in templates, you get the value of the text held in these <Name> elements.
The stylesheet TVGuide6.xsl still contains the same number of templates, but applies templates
to all the elements that it processes rather than simply getting their values.
If you run TVGuide6.xsl with TVGuide.xml you should get exactly the same result as you did before. Changing
the <xsl:value-of> elements to <xsl:apply-templates> elements hasn't altered the result of the stylesheet.
As we've seen in the previous section, the
effect of the built-in templates is that if you apply templates to an element,
you get all the text that's contained in the element, at any level. This is the
same as what you get when you use <xsl:value-of> and select the element. In other words, if you don't have a
template that matches <Program> elements or any of their descendants then the following
instructions give you exactly the same result:
<xsl:apply-templates
select="Program" />
<xsl:value-of
select="Program" />
There are
pluses and minuses to using <xsl:apply-templates> rather than <xsl:value-of>. The biggest
downside is that it's less efficient to use <xsl:apply-templates> because it forces the XSLT processor to search through the
stylesheet for templates that match the node rather than directly giving the value of the node. For this reason, I would generally only apply
templates to elements that I know could contain other elements, and not to text
nodes, attributes, or elements that I know only have text content.
On the plus side, using <xsl:apply-templates> makes it a lot easier to change the format of the value that you
get from an element, just by adding a template for that element. For example,
if I wanted to change the way I give the name of the series so that it's given
as a link to a page on that series instead, I can add a template that matches
the <Series> element and generates an
<a> element in the result giving
a link to the page.
<xsl:template
match="Series">
<a href="{.}.html">
<xsl:value-of select="." />
</a>
</xsl:template>
This
uses an attribute value template (which we met in the last chapter) to give the
URL for the page, using the value of the context node (the <Series> element) plus the string '.html'.
For example, the element <Series>EastEnders</Series> will result in a link to EastEnders.html.
This flexibility is also very useful when
the XML format that you're converting from isn't finalized. If we added <Description> elements to the <Character> and <Actor>
elements, as in TVGuide2.xml in the
code download, then we could update the stylesheet to cope with the change by
adding templates for these elements so that they only generate the value of the
<Name> rather than including the
description. We do this in TVGuide7.xsl, which contains:
<xsl:template
match="Actor">
<xsl:apply-templates select="Name" />
</xsl:template>
<xsl:template
match="Character">
<xsl:apply-templates select="Name" />
</xsl:template>
If
you didn't add these templates, then applying templates to the <Actor> or <Character>
elements would result in text containing both the name and description of the
actor or
character, concatenated.
Using templates allows you to
extend your stylesheet more easily than you can if you use <xsl:for-each> and <xsl:value-of>.
Buy Beginning XSLT here
© Copyright 2002 Wrox Press
This chapter is written by Jeni Tennison
and taken from "Beginning XSLT" published by Wrox Press Limited in June 2002; ISBN 1861005946; copyright © Wrox Press Limited 2002; all rights reserved.
No part of these chapters may be reproduced, stored in a retrieval system or transmitted in any form or by any means -- electronic, electrostatic, mechanical, photocopying, recording or otherwise -- without the prior written permission of the publisher, except in the case of brief quotations embodied in critical articles or reviews.
|
|