Re: [xsl] Conditional Assigining

Subject: Re: [xsl] Conditional Assigining
From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx>
Date: Thu, 17 May 2001 14:27:55 +0100
Hi Sri,

If you post XSLT, then it really helps if the XSLT you post matches
the source that you're supposedly using to transform it.  So for
example:

> <xsl:template match="First">

You don't have any elements called 'First' in the XML source that you
posted.  I assume that in real life this template matches the
'xslTutorial' element, or your 'xslTutorial' element in your source is
actually called 'First'.

>         <xsl:for-each select="Section[parentID='0']">

You don't have any elements called 'Section' in the XML source that
you posted - XML is case sensitive.  I assume you mean 'section' here.

>                 <xsl:variable name="ID" select="sectionID"/>
>                 <xsl:call-template name="recur">
>                         <xsl:with-param name="id">
>                                 <xsl:value-of select="sectionID" />
>                         </xsl:with-param>
>                         <xsl:with-param name="pid">
>                                 <xsl:value-of select="parentID" />
>                         </xsl:with-param>
>                         <xsl:with-param name="textvalue">
>                                 <xsl:value-of select="text" />
>                         </xsl:with-param>
>                 </xsl:call-template>

You can reduce this to:

   <xsl:call-template name="recur">
      <xsl:with-param name="id" select="sectionID" />
      <xsl:with-param name="pid" select="parentID" />
      <xsl:with-param name="textvalue" select="text" />
   </xsl:call-template>

However, since what you're doing here is just passing information in
to a template, all of which comes from a particular node, I would
suggest that you apply templates to the current node (the 'section'
element) in 'recur' mode instead:

  <xsl:apply-templates select="." mode="recur" />

The template would look like:

<xsl:template match="section" mode="recur">
   id <xsl:value-of select="sectionID" />
   pid <xsl:value-of select="parentID" />
   text <xsl:value-of select="text" />
</xsl:template>

Also, in the named template that you were using, you have:

> <xsl:template name="recur">
>         <xsl:param name="id" />
>         id <xsl:value-of select="$id"/>
>         <xsl:param name="pid" />
>         pid <xsl:value-of select="$pid"/>
>         <xsl:param name="textvalue" />
>         text <xsl:value-of select="$textvalue"/>
> </xsl:template>

The xsl:param elements must be the first elements within the template
- you can't intersperse them with the result that you want in this
way.
  
>                 <xsl:for-each select="../Section[parentID=$ID]">
>                 sub
>                         <xsl:variable name="ID" select="sectionID"/>

Here, you're binding the variable $ID for the second time within the
template - the processor should stop when it sees this, as it's an
error to reassign a value to a variable.  You should therefore get no
output when you try to run this stylesheet.

>                         <xsl:call-template name="recur">
>                                 <xsl:with-param name="id">
>                                         <xsl:value-of select="sectionID" />
>                                 </xsl:with-param>
>                                 <xsl:with-param name="pid">
>                                         <xsl:value-of select="parentID" />
>                                 </xsl:with-param>
>                                 <xsl:with-param name="textvalue">
>                                         <xsl:value-of select="text" />
>                                 </xsl:with-param>
>                         </xsl:call-template>

Similarly here you could use:

  <xsl:apply-templates select="." mode="recur" />

>                         <xsl:apply-templates select="child::*/section" />

When you apply templates here, the current node is a section element.
You're telling the processor to apply templates to the section
elements that are children (within the node tree of the source XML) of
the current section element.  Since none of the section elements have
other section elements nested within them, this does nothing.

Using the source XML that you provided, the following two templates:

<xsl:template match="xslTutorial">
   <xsl:for-each select="section[parentID = 0]">
      <xsl:apply-templates select="." mode="recur" />
      <xsl:variable name="ID" select="sectionID" />
      <xsl:for-each select="../section[parentID = $ID]">
         sub
         <xsl:apply-templates select="." mode="recur" />
      </xsl:for-each>
   </xsl:for-each>
</xsl:template>

<xsl:template match="section" mode="recur">
   id <xsl:value-of select="sectionID" />
   pid <xsl:value-of select="parentID" />
   text <xsl:value-of select="text" />
</xsl:template>

Give the result:

   id 1
   pid 0
   text section 1
         sub
         
   id 3
   pid 1
   text section 1.1
         sub
         
   id 4
   pid 1
   text section 1.2
   id 2
   pid 0
   text section 2
         sub
         
   id 6
   pid 2
   text section 2.1
         sub
         
   id 7
   pid 2
   text section 2.2

Which I think is what you were after.

If you have lots of section elements, you might want to consider using
a key to index into them by parentID more quickly, rather than
searching through them all each time.  Set the key up to match section
elements and use the parentID:

<xsl:key name="sections" match="section" use="parentID" />

And then rather than using section[parentID = ...], you can use the
key() function to get the same thing:

<xsl:template match="xslTutorial">
   <xsl:for-each select="key('sections', 0)">
      <xsl:apply-templates select="." mode="recur" />
      <xsl:for-each select="key('sections', sectionID)">
         sub
         <xsl:apply-templates select="." mode="recur" />
      </xsl:for-each>
   </xsl:for-each>
</xsl:template>

I hope that helps,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/



 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread