HomePage RecentChanges XSLT Tutorial Part 2

Continuation of XSLT Tutorial

Named Templates

You can give an xsl:template a name and then call it.

Normally we do the following:

 <xsl:apply-templates select="blah[@id = '3']"/>

Which will find any matching template, which could be...

 <xsl:template match="blah">...
 <xsl:template match="blah[@id]">
 <xsl:template match="*">
 ...

Instead we can have named templates for specific calling. It is not used as often as matched templates, but can be useful.

 <xsl:template match="/>
   <xsl:call-template name="doit"/>
 </xsl:template>
 
 <xsl:template name="doit">
   <hello>World...</hello>
 </xsl:template>

The Catch All

We often want to write XSL as a filter - something which acts on some parts of the document and then lets the rest pass through. One of the easiest ways to do that is to write in a catch all, that copies through the rest of the content.

 <xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

The match above will find any attribute (@*) or any node (better than * as it will catch evertyhing, including text).

It will then instruct to copy that element through to the next, and recursively copy the rest. Note that because it calls apply-template again, it will still actively execute your other templates.

Here is an exmample which updates owners to email address in our bugs.xml

 <?xml version="1.0" encoding="UTF-8"?>
 <xsl:stylesheet 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
 >
 
  <xsl:template match="owner">
    <a href="mailto:{.}@editure.com"><xsl:value-of select="."/>@editure.com</a>
  </xsl:template>
 
  <xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
 
 </xsl:stylesheet>

This also demonstrates a neat trick on adding content within an attribute. Add "{}" around what you would normally use in a select in an attribute. E.g.

 <a href="http://rt.internal.schools.net.au/goto?id={@id}">
    <xsl:value-of select="@id"/>
 </a>

Mode Templates and the Catch All

xhtml2to2 is a good example of how mode can be used, in combination with a catch all for use in converting XHTML2 to XHTML1.

e.g.

  <xsl:template match="@*|node()"><xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy></xsl:template>
  <xsl:template match="h:nl"><ul><xsl:apply-templates select="." mode="href"/></ul></xsl:template>
  <xsl:template match="h:name"><li class="name"><xsl:apply-templates select="." mode="href"/></li></xsl:template>
  <xsl:template match="h:line">
    <span><xsl:apply-templates select="." mode="href"/></span><br/>
  </xsl:template>

Advanced Attribute Handling

What if you want to add a attribute conditionally to an element?

You can't really use the "{}" shortuct we have above.

 <div class="@class">
   ...
 </div>

The code above will always have a class= entry, even if it is just "" - this is sometimes not desirable - we may want to leave out the class attribute altogether.

 <div>
   <xsl:if test="@class">
     <xsl:attribute name="class">
       <xsl:value-of select="@class"/>
     </xsl:attribute>
   </xsl:if>
   ...
 </div>

Warning - an xsl:attribute must be the first thing after an element that is output. You can use if, and choose, but you can't add other content first.

Once you use xsl:attribute, you can then put content inside our outside of it. You can have a xsl:choose which adds different attributes, or you can have a single attribute and have an xsl:choose inside that to select the entry. You can even call or apply templates.

Variables and Parameters

Variables

Variables are simply a named set of data that can be used within a context.

 <xsl:template match="bug">
   <xsl:variable name="email">
     <xsl:value-of select="owner"/>@editure.com.au
   </xsl:variable>

   <a href="mailto:{$email}"><xsl:value-of select="$email"/></a>
   ...
 </xsl:template>

As you can see, we can save ourselves from doing more complicated calculations if we have to use the same data multiple times. How do we know what we are getting?

You are not limited to just data, but also sets of data.

 <xsl:variable name="all" select="bug"/>
 <xsl:for-each select="$all">
   ...
 </xsl:for-each>

Parameters

Parameters can be defined by the calling method of a style sheet - e.g. Top Level parameters, or by a method of calling a template - call-template or apply-templates.

Top level templates must be defined outside of any templates, but inside the top level XML entry - xsl:stylesheet

  <xsl:param name="show_image">1</xsl:param>              <!-- Should we show the image ? -->
  <xsl:param name="show_description">1</xsl:param>        <!-- Should we show the descriptions of each item -->
  <xsl:param name="max_items">10</xsl:param>              <!-- How many items should we show -->

The above shows three parameters you can use (used as $show_image like variables) with default values. That means if the calling system (Perl XML::LibXSLT or similar) does not provide a show_image entry, then it will use the default deined here.

Passing parameters to templates is very useful.

 <xsl:template match="bug">
   <xsl:param name="strong" select="0"/>
   <xsl:value-of select="@id"> - 
   <xsl:choose>
     <xsl:when test="$strong = '1'">
       <strong><xsl:value-of select="@title"/></strong>
     </xsl:when>
     <xsl:otherwise>
       <xsl:value-of select="@title"/>
     </xsl:otherwise>
   </xsl:choose>
 </xsl:template>

Then to call the above we can do:

 <xsl:with-param name="strong">
   <xsl:if test="@severity = '3'">
     1
   </xsl:if>
 </xsl:with-param>

Advanced XPath

First or Last entry

 <?xml version="1.0" encoding="UTF-8"?>
 <xsl:stylesheet 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
 >
 
    <xsl:strip-space elements="*"/>
    <xsl:output method="text"/>
 
    <xsl:template match="bugs">
        <xsl:text>First:</xsl:text> <xsl:apply-templates select="bug[position() = 1]"/>
        <xsl:text>Last: </xsl:text> <xsl:apply-templates select="bug[position() = last()]"/>
    </xsl:template>
 
    <xsl:template match="bug">
        <xsl:text>* </xsl:text>
        <xsl:value-of select="@id"/>
        <xsl:text> - </xsl:text>
        <xsl:value-of select="@title"/>
        <xsl:text> - </xsl:text>
        <xsl:value-of select="owner"/>
	<xsl:text>(new line removed)</xsl:text>
    </xsl:template>
  
 </xsl:stylesheet>

Parent

bugsbugs.xml is a modified version of bugs, which has bugs inside bugs (have a look at attachments).

This style sheet now shows us if a parent of a bug has a hight severity.

 <?xml version="1.0" encoding="UTF-8"?>
 <xsl:stylesheet 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
 >
 
    <xsl:strip-space elements="*"/>
    <xsl:output method="text"/>
 
    <!-- We need this - or the catch all will drop everything -->
    <xsl:template match="bugs">
        <xsl:apply-templates/>
    </xsl:template>
 
    <xsl:template match="bug">
        <xsl:if test="parent::bug/@severity = '1'">
            <xsl:text>PARENT is SEV 1 - </xsl:text>
        </xsl:if>
        <xsl:text>* </xsl:text>
        <xsl:value-of select="@id"/>
        <xsl:text> - </xsl:text>
        <xsl:value-of select="@title"/>
        <xsl:text> - </xsl:text>
        <xsl:value-of select="owner"/>
	<xsl:text>(new line removed)</xsl:text>
	<xsl:apply-templates/>
    </xsl:template>
 
    <!-- Catch all - we only want to display the bugs -->
    <xsl:template match="@*|node()"/>
 
 </xsl:stylesheet>

This has introduced us a set of new problems that needed to be solved:

Child and Sibling

The same rules apply for child - what are the rules?

CGI Demo Kit

Software error:

Can't locate object method "endform" via package "CGI" at /data/scott.dd.com.au/wiki/modules/search.pl line 15.

For help, please send mail to the webmaster (webmaster@dd.com.au), giving this error message and the time and date of the error.