This is the mail archive of the
xsl-list@mulberrytech.com
mailing list .
Re: MSXML ancestor-or-self
- To: Richard Mitchell <Richard dot Mitchell at vbnonline dot com>
- Subject: Re: [xsl] MSXML ancestor-or-self
- From: Jeni Tennison <mail at jenitennison dot com>
- Date: Wed, 7 Mar 2001 19:12:01 +0000
- CC: "'xsl-list at lists dot mulberrytech dot com'" <xsl-list at lists dot mulberrytech dot com>
- Organization: Jeni Tennison Consulting Ltd
- References: <191E23677373D411BFB900D0B7A7BA28025C12@mail.vbninternal.com>
- Reply-To: xsl-list at lists dot mulberrytech dot com
Hi Richard,
> Ok my example is a bit poor sorry for that but really my context is
> C with a file like...
>
> <A>
> <P name="hello" value="blue"/>
> <P name="aval" value="uppest"/>
> <B>
> <P name="bval" value="upper"/>
> <P name="goodbye" value="red"/>
> <P name="hello" value="green"/>
> <C>
> <P name="goodbye" value="yellow"/>
> </C>
> <P name="goodbye" value="orange"/>
> </B>
> </A>
>
> Expecting output like...
> ;goodbye=yellow;hello=green;bval=upper;hello=uppest
I think that you want (a) the children of the current node and (b) the
preceding-siblings of any ancestors of the current node (or the
current node itself). So that gives you:
P | ancestor-or-self::*/preceding-sibling::P
Now, from that set of nodes, you want the final P element with a
particular 'name' attribute. So you want to cycle over those P
elements (in reverse document order) and only output information for
those P elements for which there's no following P element with the
same name attribute that's also in the list:
<xsl:variable name="Ps"
select="P | ancestor-or-self::*/preceding-sibling::P" />
<xsl:for-each select="$Ps">
<xsl:sort select="position()" data-type="number"
order="descending" />
<xsl:if test="not(following::P
[@name = current()/@name and
count(.|$Ps) = count($Ps)]">
<xsl:text />;<xsl:value-of select="@name" />
<xsl:text />=<xsl:value-of select="@value" />
</xsl:if>
</xsl:for-each>
You could also do it with a key, which may be a little more efficient
and would allow you to number the P elements if you wanted to. Define
a key to easily access the P elements by name:
<xsl:key name="Ps" match="P" use="@name" />
Give a particular name, the key will produce all the P elements in the
document with that name. To find the one that you want, you need to
filter it according to those P elements that are in your $Ps list:
key('Ps', $name)[count(.|$Ps) = count($Ps)]
and then take the last of those in document order:
key('Ps', $name)[count(.|$Ps) = count($Ps)][last()]
So to get the P elements that are the nearest, you need the hideous
expression:
$Ps[count(.|key('Ps', @name)[count(.|$Ps) = count($Ps)][last()]) = 1]
You can use this expression as the path for the xsl:for-each:
<xsl:for-each
select="$Ps[count(.|key('Ps', @name)
[count(.|$Ps) = count($Ps)][last()])
= 1]">
<xsl:sort select="position()" data-type="number"
order="descending" />
<xsl:text />;<xsl:value-of select="@name" />
<xsl:text />=<xsl:value-of select="@value" />
</xsl:for-each>
I hope that helps,
Jeni
---
Jeni Tennison
http://www.jenitennison.com/
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list