[xsl] position() from the attribute axis for getting the position of the parent in respect to its siblings

Subject: [xsl] position() from the attribute axis for getting the position of the parent in respect to its siblings
From: Abel Braaksma <abel.online@xxxxxxxxx>
Date: Thu, 18 Jan 2007 12:17:25 +0100
Dear XSLT'ers,

Somehow, I understood that position() retrieves the position of the context node relative to its siblings, in most cases at least. However, when at the attribute axis, this number is undefined, because the order in which attributes are mapped to the internal tree representation of the input is undefined.

But I am not after the position of the attribute, but I am after the position of its parent. Below are two ways to do it, however without the position() function. My question is: Is it not possible to use position() to get the "position" of the parent node when I am at the attribute axis? And if it is possible, how can I do it, what am I overlooking?

The problem appeared while doing a post-processing after some micro-pipelining. The solution provided suffices my needs, but I am still wondering why I can't seem to get position() working for me.... Below are several approaches to the problem that I tried so far.

Base template, call it on itself. It changes the 'name' attribute and replaces the text with the number, the number should equal the position of the day-node. The template match is done on the attribute axis (though in this simplified example, other approaches are possible too).

<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
version="2.0">
<xsl:output indent="yes" />
<xsl:variable name="week">
<week>
<day name="monday" />
<day name="tuesday" />
<day name="wednesday" />
</week>
</xsl:variable>
<xsl:template match="/">
<xsl:apply-templates select="$week/*" />
</xsl:template>
<!-- copy -->
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
</xsl:template>
<!-- match attribute @name -->
<xsl:template match="day/@name">
<xsl:variable name="pos">
<xsl:number select=".." />
</xsl:variable>


<!-- works -->
<xsl:attribute name="daynr0" select="$pos" />
<!-- works -->
<xsl:attribute name="daynr1"
select="count(../preceding-sibling::day | ..)" />
<!-- position on attr axis --> <xsl:attribute name="daynr2" select="position()" />
<!-- position of parent, always 1 -->
<xsl:attribute name="daynr3" select="../position()" />
<!-- position of parent opp siblings, always 1 -->
<xsl:attribute name="daynr4" select="
../../day
[generate-id(@name) = generate-id(current())]
/position()" />
<!-- position of parent opp siblings, always 1 (???) -->
<xsl:attribute name="daynr5" select="
max(
for $elem in ../../day
return
if(generate-id($elem/@name)
= generate-id(current()))
then position()
else 0)" />
</xsl:template>
</xsl:stylesheet>


-------
Output:
-------

<week>
  <day daynr0="1" daynr1="1" daynr2="1"
       daynr3="1" daynr4="1" daynr5="1"/>
  <day daynr0="2" daynr1="2" daynr2="1"
       daynr3="1" daynr4="1" daynr5="1"/>
  <day daynr0="3" daynr1="3" daynr2="1"
       daynr3="1" daynr4="1" daynr5="1"/>
</week>


As you can see, the versions "daynr0" and "daynr1" are working correctly. "daynr2" cannot possibly return something useful. I hoped for "daynr3", but I understand now that the parent axis always is one node, so this will always be one.


Leaves out daynr4, but here the predicate always selects 1 node (namely the current parent, again) and as such will always return "1" for the position.

And daynr5, which I consider correct, but also returns "1" all the time.

Is it possible to use position() in a way to get the desired results (i.e., same as daynr0 and daynr1)? Any thoughts are welcome, of course.

Cheers,
-- Abel Braaksma
  http://www.nuntia.nl

Current Thread