We've been a
little formulaic in our conversion from the simplified stylesheet to the full
stylesheet that we have now – we take every <xsl:for-each> and <xsl:value-of> and turn it into an <xsl:apply-templates>. The introduction of templates has really just
been a way of modularizing the code.
But there's another way of thinking about templates that can make your stylesheets more elegant and more
extensible, and that's to consider them as a mapping rule. Each template
describes how to map a particular node in the source XML on to a result that
you're after. In this section, we'll first look at using templates with mixed
content, and at how using templates as mapping rules is particularly useful
when processing document-oriented XML. Then, we'll go on to look at how we
could apply the same kind of technique to our stylesheet and the impact of
doing so.
Let's first look at the problem of
generating output from document-oriented XML. Document-oriented XML arises when
you take a paragraph of text and mark up particular words and phrases within that paragraph, giving mixed content – elements and text intermingled.
This is in contrast to data-oriented XML, which is concerned with storing data
and usually results in element-only and text-only content.
Take another look at the XML structure that
we're using to store information about TV programs:
<Program>
<Start>2001-07-05T19:30:00</Start>
<Duration>PT30M</Duration>
<Series>EastEnders</Series>
<Title></Title>
<Description>
Mark's health scare forces him to reconsider his future with
Lisa,
while Jamie is torn between Sonia and Zoe.
</Description>
...
</Program>
While most of the XML structure is oriented
around providing data, the description of the TV program is more
document-oriented. You could imagine wanting to add a bit of document-oriented
markup to the <Description>
element, perhaps a link to the EastEnders biography for Jamie and highlights
around the names of the characters:
<Description>
<Character>Mark</Character>'s health scare forces
him to reconsider his
future with <Character>Lisa</Character>, while
<Link
href="http://www.bbc.co.uk/eastenders/characters/jamie_m_biog.shtml">
<Character>Jamie</Character>
</Link> is torn between
<Character>Sonia</Character> and
<Character>Zoe</Character>.
</Description>
The <Description> element now holds mixed content. Looking at the node tree
representation of that piece of XML makes this clearer:
We want the elements that we use in the
description to be transformed into HTML elements instead. In the HTML version
of the page, we want to use <span> elements around the character names, and transform the <Link> elements into <a>
elements, to give:
<p>
<span
class="date">2001-07-05T19:30:00</span><br />
<span class="title">EastEnders</span><br
/>
<span class="character">Mark</span>'s
health scare forces him to
reconsider his future with <span
class="character">Lisa</span>, while
<a href="http://www.bbc.co.uk/eastenders/characters/jamie_m_biog.shtml">
<span
class="character">Jamie</span></a> is torn between
<span class="character">Sonia</span> and
<span class="character">Zoe</span>.
<span
onclick="toggle(EastEndersCast);">[Cast]</span>
</p>
If you imagine processing the content of
the <Description> element with <xsl:for-each>, you'll see that you run into problems. We haven't looked at how to
yet, but it's possible to iterate over all the nodes that are children of the <Description> element, and test what kind of node they are to decide what to do
with them. But even if you did that, you still need to take account of nested
elements, so you'd get very long and very deep conditional processing to cover
all levels of nesting.
However, with templates it's a lot easier. You can create a template for each kind of element that you
know can occur in the content of the <Description> element, which describes how to map between that element and the
result that you want. In this example, there are two elements, <Character> and <Link>, so you
need a template for each. Within the <span> and <a> elements
that these templates create, you apply templates to the content of the <Character> or <Link> element
to account for possible nested elements:
<xsl:template
match="Character">
<span class="character">
<xsl:apply-templates />
</span>
</xsl:template>
<xsl:template
match="Link">
<a href="{@href}">
<xsl:apply-templates />
</a>
</xsl:template>
Whenever you add a new type of element that
you might include in the description, you can add a new template that describes
how to map that
on to HTML.
Templates
are particularly suited to processing document-oriented XML. Each template acts
as a mapping rule from source to result.
We can add support for lots of different
elements that we want to be able to use within the <Description> element. Highlighting character names and providing links to other web sites is useful,
but you might also want to add elements for emphasis, foreign words, names of
directors, series, channels, films, and so on – different elements for the
different types of words and phrases that can appear in descriptions of TV
programs and series.
We'll add just a few of these elements in TVGuide2.xml, to create TVGuide3.xml, which looks
as follows:
<?xml
version="1.0" encoding="ISO-8859-1"?>
<TVGuide
start="2001-07-05" end="2001-07-05">
<Channel>
<Name>BBC1</Name>
...
<Program rating="5" flag="favorite">
<Start>2001-07-05T19:30:00</Start>
<Duration>PT30M</Duration>
<Series>EastEnders</Series>
<Title></Title>
<Description>
<Character>Mark</Character>'s health scare forces
him to reconsider
his future with <Character>Lisa</Character>,
while
<Link
href="http://www.bbc.co.uk/eastenders/characters/jamie_m_biog.shtml">
<Character>Jamie</Character>
</Link> is torn between
<Character>Sonia</Character> and
<Character>Zoe</Character>.
</Description>
<CastList>
<CastMember>
<Character>
<Name>Zoe Slater</Name>
<Description>
The youngest Slater girl,
<Character>Zoe</Character> really
makes the most of the fact she's the baby of the
family.
</Description>
</Character>
<Actor>
<Name>Michelle Ryan</Name>
<Description>
For more details, see
<Link
href="http://www.ajmanagement.co.uk/michelle-ryan.htm">
<Actor>Michelle Ryan</Actor>'s Agency
</Link>.
</Description>
</Actor>
</CastMember>
<CastMember>
<Character>
<Name>Jamie Mitchell</Name>
<Description>
Jamie's a bit of a heartthrob (who could resist that
little-boy-lost look?) but until
<Character>Janine
Butcher</Character> came along he'd steered clear
of girls.
</Description>
</Character>
<Actor>
<Name>Jack Ryder</Name>
<Description>
Won Best Newcomer for <Character>Jamie
Mitchell</Character>
in the 1999 TV awards.
</Description>
</Actor>
</CastMember>
<CastMember>
...
</CastMember>
</CastList>
...
</Program>
...
</Channel>
...
</TVGuide>
Try using TVGuide6.xsl with TVGuide3.xml, which
uses <xsl:apply-templates> to apply templates to the <Description> element. There aren't any templates for <Character> or <Link>
elements, so the built-in templates are used instead. The result of the
transformation of the <Description> element looks just the same as before, because the built-in
templates automatically show any text within an element.
We want a new version of the stylesheet (TVGuide8.xsl), which generates HTML where the words and phrases in the
description that we've picked out with <Character> and <Link>
elements are displayed and behave slightly differently from the rest of the
text. Links should be links, for
example, and character names should be slightly larger than the surrounding
text.
To make the marked-up text display and act
differently, we need to introduce templates for these new elements: one for the
<Link> element, to create a
hypertext link with an HTML <a>
element:
<xsl:template
match="Link">
<a href="{@href}">
<xsl:apply-templates />
</a>
</xsl:template>
and one for the <Character> element, to create a <span> element with a class of character
around the character names:
<xsl:template
match="Character">
<span class="character">
<xsl:apply-templates />
</span>
</xsl:template>
To make the character names slightly
bigger, we'll add a rule to TVGuide.css, to create TVGuide2.css, which contains:
.character {
font-size: larger;
}
Putting the final touches on TVGuide8.xsl, we need the HTML that it creates to point to TVGuide2.css rather than TVGuide.css, so the <link> element
generated in the template matching the root node needs to be altered slightly:
<xsl:template
match="/">
<html>
<head>
<title>TV Guide</title>
<link rel="stylesheet"
href="TVGuide2.css" />
...
</head>
...
</html>
</xsl:template>
Having made this final change, transform TVGuide3.xml with TVGuide8.xsl. The
result of the transformation should look something like the following:
The character names are slightly larger
than the rest of the text, and clicking on the word "Jamie" takes you to the EastEnders site with Jamie Mitchell's biography.
Feel free to add your own elements to that description, and add your own
templates matching them to present the document-oriented XML.
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.
|
|