I have recently started using XSLT for some of my XML documents and I have some questions. I add the code below. In the code I have a template that matches ebook elements. I then want to list all the authors that wrote the book. I do it using a for each loop, but I could also apply a template to it. I can't see a clear line when to use loops and when to use templates.
And another question is it normal to just say apply-templates when you now that there wont be other children of the element where you are writing it. In my case in the template that matches the document root I say apply-templates. Then it finds ebooks which is the only child of it, but I could have a "books" element which distinguish between "regular" books, and electronic books then it would just list the character data of the books. I then would have needed to write apply-templates select="ebooks" if I just wanted the ebooks in my final document. So is this a case of that it depends of how well you know your document?
Thank you, here is my code (This is just for practicing):
XML:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="ebooks.xsl"?>
<ebooks>
<ebook>
<title>Advanced Rails Recipes: 84 New Ways to Build Stunning Rails Apps</title>
<authors>
<author><name>Mike Clark</name></author>
</authors>
<pages>464</pages>
<isbn>978-0-9787-3922-5</isbn>
<programming_language>Ruby</programming_language>
<date>
<year>2008</year>
<month>5</month>
<day>1</day>
</date>
<publisher>The Pragmatic Programmers</publisher>
</ebook>
...
XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<html>
<head>
<title>Library</title>
</head>
<body>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="ebooks">
<h1>Ebooks</h1>
<xsl:apply-templates>
<xsl:sort select="title"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="ebook">
<h3><xsl:value-of select="title"/></h3>
<xsl:apply-templates select="date" />
<xsl:for-each select="authors/author/name">
<b><xsl:value-of select="."/>,</b>
</xsl:for-each>
</xsl:template>
<xsl:template match="date">
<table border="1">
<tr>
<th>Day</th>
<th>Month</th>
<th>Year</th>
</tr>
<tr>
<td><xsl:value-of select="day"/></td>
<td><xsl:value-of select="month"/></td>
<td><xsl:value-of select="year"/></td>
</tr>
</table>
</xsl:template>
</xsl:stylesheet>
The apply template helps in processing the order for instance let's take if we want to list the place of the people in the source document and we happened to place the address first, regardless of the order in which the input document occurs. It is required to name a template.
The <xsl:apply-templates> element applies a template to the current element or to the current element's child nodes. If we add a "select" attribute to the <xsl:apply-templates> element, it will process only the child elements that matches the value of the attribute.
The xsl:call-template element is used to invoke a template by name. By invoke, we mean that the named template is called and applied to the source document. If a template does not have a name, it cannot be called by this element. The xsl:template element is used to create a template.
This question is a bit argumentative, but here is my take on it.
I can't see a clear line when to use loops and when to use templates.
I'd say that you shoud strive to avoid for-each
as often as possible in favor of apply-templates
.
Using for-each
makes your program more complex by adding nesting levels and it's also impossibe to re-use the code inside the for-each
block. Using apply-templates
will (when done right) generate more more flexible and modular XSLT.
On the other hand: If you write a stylesheet with limited complexity and re-usability or modualarization are not a concern, using for-each
may be quicker and easier to follow (for a human reader/maintainer). So it's partly a question of personal preference.
In your case, I would find this elegant:
<xsl:template match="ebook">
<!-- ... -->
<xsl:apply-templates select="authors/author" />
<!-- ... -->
</xsl:template>
<xsl:template match="authors/author">
<b>
<xsl:value-of select="name"/>
<xsl:if test="position() < last()">,</xsl:if>
</b>
</xsl:template>
To your other question
And another question is it normal to just say
apply-templates
when you know that there won't be other children of the element where you are writing it.
When you know there will never be any children in this element, writing apply-templates
is pointless.
When done right, XSLT has the ability to flexibly deal with varying input. If you expect there could be children at some point, an apply-templates
won't hurt.
A simple apply-templates
without select
is rather unspecific, both in terms of which (i.e.: all of them) and in what order (i.e.: input document order) nodes will be processed. So you could end up either processing nodes you never wanted to process or node you already have processed earlier.
Since one cannot write a sensible template for unknown nodes, I tend to avoid the unspecific apply-templates
and just adapt my stylesheet when the input changes.
I do it using a for each loop, but I could also apply a template to it. I can't see a clear line when to use loops and when to use templates.
Using <xsl:for-each>
is in no way harmful if one knows exactly how an <xsl:for-each>
is processed.
The trouble is that a lot of newcomers to XSLT that have experience in imperative programming take <xsl:for-each>
as a substitute of a "loop" in their favorite PL and think that it allows them to perform the impossible -- like incrementing a counter or any other modification of an already defined <xsl:variable>
.
One indispensable use of <xsl:for-each>
in XSLT 1.0 is to change the current document -- this is often needed in order to be able to use the key()
function on a document, different from the current source XML document, for example to efficiently access lookup-table that resides in its own xml document.
On the other side, using <xsl:template>
and <xsl:apply-templates>
is much more powerful and elegant.
Here are some of the most important differences between the two approaches:
xsl:apply-templates
is much richer and deeper than xsl:for-each
, even
simply because we don't know what code will be applied on the nodes of
the selection -- in the general case this code will be different for
different nodes of the node-list.
The code that will be applied
can be written way after the xsl:apply template
s was written and by
people that do not know the original author.
The FXSL library's implementation of higher-order functions (HOF) in XSLT wouldn't be possible if XSLT didn't have the <xsl:apply-templates>
instruction.
another question is it normal to just say apply-templates when you (k)now that there wont be other children of the element where you are writing it
<xsl:apply-templates/>
is a shorthand for:
<xsl:apply-templates select="child::node()"/>
Even if there are other children of the current node, about which you dont't care, you coud still use the short <xsl:apply-templates>
and have another template like that:
<xsl:template match="*"/>
This template ignores ("deletes") any element. You should override it with more specific templates (in XSLT, generally, more specific templates have higher priority and get selected for processing over less-specific templates matching the same node):
<xsl:template match="ebook">
<!-- Necessary processing here -->
</xsl:template>
I usually don't use <xsl:template match="*"/>
but I use another template that matches (and ignores) every text node:
<xsl:template match="text()"/>
This has typically the same effect as using <xsl:template match="*"/>
, because of the way XSLT processes nodes for which there is no matching template. In any such case XSLT uses its built-in templates in what may be called "default processing.
The result of XSLT's default processing of a subtree rooted in an element is to output the concatenation (in document order) of all text-node descendents of this element.
Therefore, preventing the output of any text node using <xsl:template match="text()"/>
has the same (null) output result as preventing output from processing an element by using <xsl:template match="*"/>
.
Summary:
Templates and the <xsl:apply-templates>
instruction is how XSLT implements and deals with polymorphism.
It is in the spirit of XSLT's processing model to use more generic templates matching large classes of nodes and doing some default processing for them and later overriding the generic templates with more specific ones that match and process only nodes we are interested in.
Reference: See this whole thread: http://www.stylusstudio.com/xsllist/200411/post60540.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With