Re: [xsl] Using the key function

Subject: Re: [xsl] Using the key function
From: Abel Braaksma <abel.online@xxxxxxxxx>
Date: Wed, 23 May 2007 15:55:44 +0200
Hi Karl,

see my comments, and below for sample code:



Fanghanel, Karl wrote:
My xsl code does work giving all the values under the house element as

your XSL code is not valid XSL. It cannot possibly run, so it cannot produce the output you say it does.


well as some others, but I'm not able to get the length value from the
corresponding trail element. The trail element contains information
belonging to the house element and is associated by matching the ref and
id values. The only way I know how to do this is by using the key
function, but it's not working.

No, you just have to find the one that has the @id the same as the @ref as the current member element. No need for a key.


I can't easily change the group-by function because that is what is used
in production and I can't change the "property[@name='number']/@val"
because all the other elements (not shown) are grouped by this unique
property except the trail element.


Your group-by function (this is not a function, it is the for-each-group instruction) groups on one element, the root element, so essentially it does nothing.



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


<xsl:output method="html"/>

<xsl:key name="numkey" match="//house" use="member/@ref"/>

Like David already pointed out in an earlier reply, it does not make sense using '//' in a key. A key indexes every element in the document that matches the @match, using // is thus redundant.


<html>
<body>
<table ... (table format)

You cannot have an LRE on the root level.


<xsl:for-each-group select="//project" group-
by="property[@name='number']/@val">
<xsl:sort select="@number"/>

There is no @number in the project element. There is no property element as a child of a project element. You only have one project element, the root element without any direct child attributes. The result is that your xsl:for-each-group does nothing (it executes once).


<tr>
<td>
<xsl:value-of
select="current-grouping-
key()"/>

This will be empty, see above


</td>
<td>
<xsl:value-of
select="@name"/>

There is no @name attribute on the project element.


</td>
<td>
<xsl:value-of
select="key('numkey',@id)/@length"/>

The project element does not have an @id attribute. This will select nothing.


</td>
</tr>
</xsl:for-each-group>
</table>
</body>
</html>
</stylesheet>

a stylesheet is closed with an </xsl:stylesheet> closing tag. It seems to me that you did not test your own code before you send it.


I tried to understand you specs from what I saw and what the XPaths may have implied in your original code and context. This is all guessing work. But the following stylesheet, when run on your input, produces the following output:

<html>
  <body>
     <table>
        <tr>
           <td>123L0001</td>
           <td>1_CAD</td>
           <td>50.5</td>
        </tr>
        <tr>
           <td>123L0002</td>
           <td>2_CAB</td>
           <td>40.0</td>
        </tr>
     </table>
  </body>
</html>

I removed any instructions that did not make any sense with your input. I decided to group on the house elements, because that is what your body of your group-instruction seems to imply. It also seems to me that your group-by clause may give you unexpected results when it selects nothing (i.e., when there is no property element with the @name attribute having 'number').

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

<xsl:output method="html"/>

   <xsl:template match="/">
       <html>
           <body>
               <table>
                   <xsl:for-each-group
                       select="$input/project/house"
                       group-by="property[@name='number']/@val">

                       <xsl:apply-templates select="." />
                   </xsl:for-each-group>
               </table>
           </body>
       </html>
   </xsl:template>

<xsl:template match="house">
<tr>
<td>
<xsl:value-of select="current-grouping-key()"/>
</td>
<td>
<xsl:value-of select="@name"/>
</td>
<td>
<xsl:value-of select="../trail[@id = current()/member/@ref]/@length"/>
</td>
</tr>
</xsl:template>


</xsl:stylesheet>


HTH, if not, please provide more details on what output you expect, what output you have now, and above all, a *working* sample of your incorrect code.


Cheers,
-- Abel Braaksma

Current Thread