Re: [xsl] XSL-T should naturally loop? not grabbing all the children node-sets..

Subject: Re: [xsl] XSL-T should naturally loop? not grabbing all the children node-sets..
From: Abel Braaksma <abel.online@xxxxxxxxx>
Date: Tue, 22 Apr 2008 16:47:44 +0200
Hi Dan,

Before diving into the real problem, let's try to get your code a bit tidied up. Some of my comments may be of a more advanced nature than your current abilities of XSLT are, but feel free to ask any questions. See below.



Dan Acuff wrote:
<?xml version="1.0" encoding="iso-8859-1"?>

Unless there's a compelling reason to do so, it is generally best to stick to one of the Unicode encodings, UTF-8 for example, it will save you a lot of encoding problems later. Note that the XML encoding of the XSLT file has nothing to do with the output or source encoding but only applies to the XML file containing the declaration.


<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
version="1.0">

If you can, try XSLT 2.0. Many people find it way easier to learn than XSLT 1.0. This is for one due to the fact that version 1.0 made it very hard to do string processing or to (re)process intermediate result trees.


<xsl:output encoding="iso-8859-1" method="html"/>

Same story about encoding. If you target web browsers, you make it easier on yourself and the browsers to encode using UTF-8. Again, the encoding of the source does not matter, any transformation needed from one encoding to the other will be done by the processor.


<xsl:param name="paraCatagory"/>

I assume you set this name on the commandline? Otherwise, its default is empty.


<xsl:template match="menu">
<xsl:apply-templates/>
</xsl:template>

Good. Your starting point. If the menu-element may also appear deeper down the tree then match="/menu" might be safer.


<xsl:template match="category">
<xsl:if test="(@name=$paraCatagory)">

Here you put an xsl:if around a full template. That's hardly ever necessary. Put it in the predicate:


<xsl:template match="category[@name = $paraCategory]" >...

and don't forget the generic throw-away match if you don't want other hits to output anything:

<xsl:template match="category">
<xsl:apply-templates /> <!-- remove this if you do not want the children of other categories to be processed -->
</xsl:template>


<xsl:for-each select=".">

this does nothing. You want to iterate over all elements in the selection of the current element, which is the current element, which is always one element: the current ;) Just remove the whole statement.


<xsl:element name="div">
<xsl:attribute name="class">cat_block</xsl:attribute>
<xsl:element name="div">
<xsl:attribute name="class">cat_module</xsl:attribute>

This and related should be (not necessarily "should") rewritten as LRE (Literal Result Elements) when you do not need their names ("div", "class") to be generated dynamically:


<div class="cat_block">
   <div class="cat_module">

that will together make the whole code quite a bit easier to read.
<xsl:value-of select="category/@display_name"/>
<br/>
<xsl:element name="a">
<xsl:attribute name="href">
<xsl:value-of select="category/info/link"/>
</xsl:attribute>

and the same here, but here you should use AVT (Attribute Value Templates):


<a href="{category/info/link}">

note that if there are more than one category or more than one info or more than one link element, that in XSLT 1.0 this will select the first element of each of them, in XSLT 2.0 it will give an error. In that case it is better to write it like so:

<a href="{category[1]/info[1]/link[1]}">

(depending on which elements can have more than one in the source)

</snip>

what I fail to see is a nesting <xsl:apply-templates /> which is the reason that only one category is being processed by your code. Either place the apply-templates at the end (to select siblings) or at the middle, to recursively build a tree (but then I assume you should select children, which is the default).


Finally, you are currently only matching category and menu elements. If the processor encounters other elements, the default template will be called which, by default, will output the text values of these elements. If you don't want that but you do want the processing to continue through the children and all other descendants, add this:

<xsl:template match="*">
  <xsl:apply-templates />
</xsl:template>

which will be called whenever nothing is matched through the more specific templates.


I haven't delved futher into your problem yet. Most likely, when you fix these things and tidy up your code it will just show at once. Otherwise, feel free to ask further once you've fixed this part.


Cheers,
-- Abel Braaksma

Current Thread