Using templates as mapping rules really
makes explicit the correspondence between a bit of the source XML and the
result that you desire from it. As we've seen in TVGuide14.xsl, we can adopt this approach in data-oriented XML as well as
document-oriented XML – you can have different templates matching different
elements, even if those elements follow the traditionally 'data-oriented'
pattern of just having element or text children. The approach that we were
working with at the start of this chapter was quite different. There, we had
very few templates (we started with just one!), and where we did use them it
was really to make the stylesheet more manageable and to get reuse.
These two approaches to transformations
with XSLT are termed push and pull.
In the push approach, templates specify
fairly low-level rules and the source XML document gets pushed through the
stylesheet to be transformed on the way. Stylesheets that use the push approach
tend to have a lot of templates, each containing
a snippet of XML with an <xsl:apply-templates> instruction that moves the processing down to all an element's
children. The final structure
of the result is highly determined by the structure of the source.
Here's an example of a stylesheet, TVGuide15.xsl, which demonstrates a push approach. You can see how multiple
templates are used to build up the result, but without knowing the structure of
the source XML document it's hard to tell exactly what result you'll get (I've
highlighted the main changes from TVGuide14.xsl, though it was actually quite push-like already):
<?xml version="1.0"
encoding="ISO-8859-1"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template
match="/">
<html>
<head>
...
</head>
<body>
<h1>TV Guide</h1>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template
match="Channel/Name">
<h2 class="channel"><xsl:apply-templates
/></h2>
</xsl:template>
<xsl:template match="Program">
<div>
<p>
<xsl:apply-templates select="Start" /><br
/>
<xsl:apply-templates
select="Series" /><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" />
</ul>
</div>
</div>
</xsl:template>
<xsl:template
match="Start">
<span class="date"><xsl:apply-templates
/></span>
</xsl:template>
<xsl:template match="Series">
<span class="title"><xsl:apply-templates
/></span>
</xsl:template>
<xsl:template match="CastMember">
<li><xsl:apply-templates /></li>
</xsl:template>
<xsl:template match="Character">
<span class="character"><xsl:apply-templates
/></span>
</xsl:template>
<xsl:template match="Actor">
<span class="actor"><xsl:apply-templates
/></span>
</xsl:template>
<xsl:template
match="Character/Description" />
<xsl:template
match="Actor/Description" />
<xsl:template match="Description//Character">
<span
class="character"><xsl:apply-templates /></span>
</xsl:template>
<xsl:template match="Description//Actor">
<span
class="actor"><xsl:apply-templates /></span>
</xsl:template>
<xsl:template match="Description//Link">
<a
href="{@href}"><xsl:apply-templates /></a>
</xsl:template>
<xsl:template
match="Description//Program">
<span class="program"><xsl:apply-templates
/></span>
</xsl:template>
<xsl:template match="Description//Series">
<span class="series"><xsl:apply-templates
/></span>
</xsl:template>
<xsl:template
match="Description//Channel">
<span class="channel"><xsl:apply-templates
/></span>
</xsl:template>
</xsl:stylesheet>
Note
that in this stylesheet I've used empty templates to do nothing with the <Description> elements within <Character> and <Actor>
elements within the cast list. I apply templates to both the <Name> and <Description>
children of these elements, but then ignore the <Description> element. This contrasts with a pull approach, which would only
apply templates to the <Name> element in the first place.
In the pull approach, the stylesheet pulls
in information from the source XML document to populate a template structure.
Stylesheets that use the pull
approach tend to have only a few templates and to use <xsl:for-each> and <xsl:value-of>
to generate the result. The final structure of the result is mainly determined
by the structure of the stylesheet and how the templates fit together.
Here's an example of a stylesheet that
demonstrates a pull approach (actually, it's TVGuide2.xsl – the first stylesheet we used in this chapter, more or less). As
you can see, there's only one template and its content follows the structure of
the result that it generates very closely:
<?xml
version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template
match="/">
<html>
<head>
...
</head>
<body>
<h1>TV Guide</h1>
<xsl:for-each select="/TVGuide/Channel">
<h2 class="channel"><xsl:value-of
select="Name" /></h2>
<xsl:for-each select="Program">
<div>
<p>
<span class="date"><xsl:value-of
select="Start" /></span>
<br />
<span class="title"><xsl:value-of
select="Series" /></span>
<br />
<xsl:value-of select="Description" />
<span
onclick="toggle({Series}Cast);">[Cast]</span>
</p>
<div id="{Series}Cast" style="display:
none;">
<ul class="castlist">
<xsl:for-each
select="CastList/CastMember">
<li>
<span class="character">
<xsl:value-of
select="Character/Name" />
</span>
<span class="actor">
<xsl:value-of
select="Actor/Name" />
</span>
</li>
</xsl:for-each>
</ul>
</div>
</div>
</xsl:for-each>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
If you look carefully, you'll notice that I
haven't even tried to style the content of the <Description> element in this stylesheet – it's very hard to process
document-oriented XML with a pull style.
Push and pull approaches both have their
own advantages and disadvantages, and the best stylesheets use both approaches
in tandem to process different
parts of a particular XML document. You might use pull for the data-oriented
parts of the XML and push for the document-oriented parts, for example. Here
are some general guidelines about where to use push and where to use pull:
Use multiple templates, each matching
different types of nodes (a push approach) to process document-oriented XML.
Select the nodes that you specifically
want to process (a pull approach) to change the order in which the source is
processed or to only process certain portions of the source. This is very
common in data-oriented XML where the order in which information is contained
in the source is not the same as the order in which it is required in the result.
Apply templates to nodes, rather than
getting their value directly (a push approach) to allow for extensibility in
areas where the structure of the source XML, or the result that you want to
generate from it, might change in the future.
Access the values of nodes directly (a
pull approach) when the structure of the source XML is fixed and you know
exactly what result you want from it.
Taking these guidelines into account, TVGuide16.xsl gives a stylesheet that balances the push and pull approaches. It
mainly uses a push style, but selects nodes directly for processing in certain
places, notably to get only the <Name> child of <Character>
and <Actor> elements and ignore the <Description>.
Using templates to match nodes
is a push approach, which is good for document-oriented XML and for
extensibility. Selecting nodes to process is a pull approach, which is good for
changing the structure of a document.
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.
|
|