Jeni Tennison
>Unless I've missed an easier design,
>one basically has to walk the tree up to the root node, to grab the location
>path. At that point, the location path is in reverse, so one must write
>another recursive template to switch the order. That's the part I'm doing
>now, as the first part was pretty easy. It only constructs it in the wrong order if you recurse in the wrong place.
If you do:
<xsl:template match="*" mode="path">
<xsl:text>/</xsl:text>
<xsl:value-of select="name()" />
<xsl:apply-templates select="parent::*" mode="path" />
</xsl:template>
or something similar, then you are outputting the step for each parent
*after* the step for its child, so the path is the wrong way round. If, on
the other hand, you do: <xsl:template match="*" mode="path">
<xsl:apply-templates select="parent::*" mode="path" />
<xsl:text>/</xsl:text>
<xsl:value-of select="name()" />
</xsl:template>
then you generate the information for the parent before the information for
the child, which gives you the hierarchy that you want.
It is actually possible to do this without recursion because the
ancestor-or-self axis gives you a list of the nodes that are ancestors of
the current node (or the current node itself). You can *iterate* over this
list instead:
<xsl:for-each select="ancestor-or-self::*">
<xsl:text>/</xsl:text>
<xsl:value-of select="name()" />
</xsl:for-each>
You only need to do recursion when the hierarchy path that you're
constructing is not the same as the hierarchy path that you have in your
source. This happens when, for example, you have a flat structure
describing a number of classes each of which have attributes giving links
to the parent class.
In addition you should bear in mind that the paths you create using this
method do not give you exact directions to the node that you want. To do
so, you should add predicates to the path indicating the position of the
node relative to those of its siblings with the same name or use some other
method to identify the unique properties of a particular element (e.g. the
value of an attribute, particularly an ID attribute).
Wendell Piez adds: It works this way because xsl:for-each, by default, processes the selected
nodes in document order, which is down from the root, even though the axis
itself goes in reverse. This is pretty subtle.
You can change the order of its processing by using a sort. So (again,
without the recursion) to get them in reverse order, we could use
<xsl:for-each select="ancestor-or-self::*">
<xsl:sort select="count(ancestor::*)" order="descending"/>
<xsl:value-of select="concat('/', name())"/>
</xsl:for-each>
|