Re: [xsl] Getting variable yet most immediate parentNode

Subject: Re: [xsl] Getting variable yet most immediate parentNode
From: Abel Braaksma <abel.online@xxxxxxxxx>
Date: Fri, 28 Sep 2007 02:00:16 +0200
Wendell Piez wrote:



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

Actually this should be "ancestor::*/answers", or "ancestor::*[answers][1]/answers", shouldn't it?


".//answers" will search the subtree below the current node, rather than look up the tree.

"ancestor::*/answers" will select answers elements from all ancestors; "ancestor::*[answers][1]/answers" will select only the closest one. If you think the latter expression is too opaque, you could also write

(ancestor::*/answers)[last()]

... and then try to explain that. :-)


Awe, yes, you are right of course! I always tell myself not to send out technical messages in the middle of the night, even more so, not when there's some beer around. But looking again now at his code, Steve constructs one <div> element for each option element which is a child of <questions> element. Which is interesting, because in his supplied source, there isn't any at that level:


 <questions>
     <answers />
     <question>
        <answer>
            <option>True</option>
            <option>False</option>
        </answer>
     </question>
 </questions>


which means that <xsl:template match="option" mode="q">... will never be triggered by this snippet:


   <xsl:template match="questions">
       <xsl:apply-templates mode="q" select="option" />
       <SNIP />
   </xsl:template>


Following up the analysis, the <xsl:template match="answers" > contains an xsl:if test="option" which can never be true either, considering the sample source, the xsl:if encompassing the complete code block:


   <xsl:template match="answers">
       <xsl:param name="qKey" />
       <xsl:if test="option">
   .........


Steve, if you rewrite statements like that as this one above as:


   <xsl:template match="answers[option]">
      ....

meaning: without the xsl:if completely, it will do the same and better. You would benefit a great deal for clarity and you would much clearer see and find where your code goes wrong. The way it is now it probably doesn't output at all what you want. While rewriting, you will find the problems and you can more easily fix them.


Wendell, considering the possible intent of Steve to wrap all output inside one <div>, it is probably better if he does go back to using the // syntax, but not in the way I proposed, but more like this (assuming that the match="option" was a mistake from Steve, it doesn't even have an @name, I replace it here with "section"):


<xsl:template match="section">
<div>
<span class="two"><label>
<xsl:value-of select="(@name | .)[1]" /> <!-- xslt 1.0 syntax now -->
</label></span>
<xsl:apply-template select=".//answers" />
</div>
</xsl:template>


<xsl:template match="answers[option]" >
    <select>
      <xsl:apply-templates select="option" />
    </select>
</xsl:template>

<xsl:template match="option">
   <option><xsl:value-of select="(@name | .)[1]" />
</xsl:template>

etc. etc. This roughly does the same as the whole structure that Steve posted, but I left out the details (and some late-hour mistakes are probably there: finders keepers). Steve, as you can see, you don't need to test for the existence of a node before you use it. Just use apply-templates, it does the "if"-ing for you.

Cheers,
-- Abel Braaksma

Current Thread