tags:

views:

186

answers:

3

I have a document that looks something like

<root>
    <element>
        <subelement1 />
        <subelement2 />
    </element>
    <element>
        <subelement2 />
        <subelement1 />
    </element>
</root>

In my XSLT sheet in the context of /element[2]/[someNode] I want to get a number that represents the distance of /element[1]/[someNode] (ie, the number of preceding siblings of /element1/[someNode]). For example, in the context of /element[2]/subelement1 I'd like to have some way to get the number 2, the distance from /element[1] to /element[1]/subelement2. I only ever need the distance of the given node name from the first instance of <element>.

Intuitively I thought I could construct this like

 <xsl:variable name="nodename" select="name()" />
 <xsl:value-of select="/element[1]/$nodename/preceding-sibling::*" />

but unfortunately this sheet doesn't compile. Is what I'm trying to achieve possible in XSLT?

+1  A: 

*[name() = $nodename] might be what you want instead of $nodename but you should then better define two variables with the value of local-name() and namespace-uri() and check e.g. *[local-name() = $localname and namespace-uri() = $namespaceuri] to have a namespace safe way of selecting elements.

Martin Honnen
A: 

Build xpath's is unfortunately impossible. XPaths are statically compiled; you cannot generate them on the fly (or if you do, they're just strings and cannot be executed).

However, what you can do is write a query that happens to cross-reference several values which themselves are dynamic...

Eamon Nerbonne
+3  A: 
  1. You cannot use an XSLT variable as the axis of an XPATH statement, but you can use it in a predicate filter. So, if you were to match on any element (i.e. *) and then restrict it to elements who's name() is equal to the value stored in your variable (i.e. *[name()=$nodename]) the XPATH will be valid.

  2. The XPATH you were constructing would return the value of the matching element. If you want to return how many elements match that pattern, you can use the count() function.

  3. Your example XML has a document element <root>, but your XPATH does not include <root>.

This returns the number of preceding-sibling elements using the variable assigned by the context node:

<xsl:variable name="nodename" select="name()" />
<xsl:value-of select="count(/root/element[1]/*[name()=$nodename]/preceding-sibling::*)" />

You could eliminate the variable and just use:

<xsl:value-of select="count(/root/element[1]/*[name()=name(current())]/preceding-sibling::*)" />
Mads Hansen