Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

For loops vs. apply-templates

Tags:

xml

xslt

xslt-1.0

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>
like image 438
LuckyLuke Avatar asked Jun 14 '11 11:06

LuckyLuke


People also ask

What does apply template do?

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.

What does xsl apply templates select mean?

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.

What is a call template?

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.


2 Answers

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() &lt; 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.

like image 60
Tomalak Avatar answered Sep 18 '22 15:09

Tomalak


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:

  1. 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.

  2. The code that will be applied can be written way after the xsl:apply templates 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:

  1. Templates and the <xsl:apply-templates> instruction is how XSLT implements and deals with polymorphism.

  2. 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

like image 25
Dimitre Novatchev Avatar answered Sep 16 '22 15:09

Dimitre Novatchev