Aftershock
Aftershock

Reputation: 5351

How to store the current path in xsl?

I would like to store the path of the current node so I can reused it in an expression in XSLT. Is it possible?

<!-- . into $path? -->    
<xsl:value-of select="$path" />

Upvotes: 2

Views: 1837

Answers (3)

user357812
user357812

Reputation:

As @Dimitre and @Tomalak have point out, I don't think it has some value in the same transformation to obtain a string representing an XPath expression for a given node, and then select the node "parsing" such string. I could see some value in performing those operations in different transformations.

Besides that, this stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes"/>
    <xsl:template match="/">
        <xsl:for-each select=".|//node()|//@*">
            <xsl:variable name="vPath">
                <xsl:apply-templates select="." mode="getPath"/>
            </xsl:variable>
            <xsl:value-of select="concat($vPath,'&#xA;')"/>
            <xsl:call-template name="select">
                <xsl:with-param name="pPath" select="$vPath"/>
            </xsl:call-template>
            <xsl:text>&#xA;</xsl:text>
        </xsl:for-each>
    </xsl:template>
    <xsl:template match="/|node()|@*" mode="getPath" name="getPath">
        <xsl:apply-templates select="parent::*" mode="getPath"/>
        <xsl:text>/</xsl:text>
        <xsl:choose>
            <xsl:when test="self::*">
                <xsl:value-of select="concat(name(),'[',
                                             count(preceding-sibling::*
                                                     [name() =
                                                      name(current())]) + 1,
                                             ']')"/>
            </xsl:when>
            <xsl:when test="count(.|../@*)=count(../@*)">
                <xsl:value-of select="concat('@',name())"/>
            </xsl:when>
            <xsl:when test="self::text()">
                <xsl:value-of
                     select="concat('text()[',
                                    count(preceding-sibling::text()) + 1,
                                    ']')"/>
            </xsl:when>
            <xsl:when test="self::comment()">
                <xsl:value-of
                     select="concat('comment()[',
                                    count(preceding-sibling::comment()) + 1,
                                    ']')"/>
            </xsl:when>
            <xsl:when test="self::processing-instruction()">
                <xsl:value-of
                     select="concat('processing-instruction()[',
                                    count(preceding-sibling::
                                             processing-instruction()) + 1,
                                    ']')"/>
            </xsl:when>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="select">
        <xsl:param name="pPath"/>
        <xsl:param name="pContext" select="/"/>
        <xsl:param name="pInstruction" select="'value-of'"/>
        <xsl:variable name="vPosition"
                      select="number(
                                 substring-before(
                                    substring-after($pPath,
                                                    '['),
                                    ']'))"/>
        <xsl:variable name="vTest"
                      select="substring-before(
                                 substring-after($pPath,
                                                 '/'),
                                 '[')"/>
        <xsl:variable name="vPath" select="substring-after($pPath,']')"/>
        <xsl:choose>
            <xsl:when test="$vPath">
                <xsl:call-template name="select">
                    <xsl:with-param name="pPath" select="$vPath"/>
                    <xsl:with-param name="pContext"
                                    select="$pContext/*[name()=$vTest]
                                                          [$vPosition]"/>
                    <xsl:with-param name="pInstruction"
                                    select="$pInstruction"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:variable name="vContext"
                              select="$pContext/node()
                                          [self::*[name()=$vTest]|
                                           self::comment()[$vTest='comment()']|
                                           self::text()[$vTest='text()']|
                                           self::processing-instruction()
                                              [$vTest =
                                               'processing-instruction()']]
                                          [$vPosition]|
                                      $pContext[$pPath='/']|
                                      $pContext/@*[name() =
                                                   substring($pPath,3)]
                                                  [not($vTest)]"/>
                <xsl:choose>
                    <xsl:when test="$pInstruction='value-of'">
                        <xsl:value-of select="$vContext"/>
                    </xsl:when>
                    <xsl:when test="$pInstruction='copy-of'">
                        <xsl:copy-of select="$vContext"/>
                    </xsl:when>
                </xsl:choose>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

With this input:

<?somePI pseudoAttributes?>
<root>
    <!-- This is a comment -->
    <node attribute="Value">text</node>
</root>

Output:

/
  text 
/processing-instruction()[1]
pseudoAttributes
/root[1]
  text 
/root[1]/comment()[1]
 This is a comment 
/root[1]/node[1]
text
/root[1]/node[1]/@attribute
Value
/root[1]/node[1]/text()[1]
text

Upvotes: 0

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243599

Hi, I would like to store the path of the current node so I can reused it in an expression in XSLT. Is it possible?

It is possible for any given node to construct an XPath expression that, when evaluated, selects exactly this node. In fact more than one XPath expression exists that selects the same node.

See this answer for the exact XSLT code that constructs such an XPath expression.

The problem is that this XPath expression cannot be evaluated during the same transformation in XSLT 1.0 or XSLT 2.0, unless the EXSLT extension function dyn:evaluate is used (and very few XSLT 1.0 processors implement dyn:evaluate() ).

What you want can be achieved in an easier way in XSLT using the <xsl:variable> instruction:

<xsl:variable name="theNode" select="."/>

This variable can be referenced anywhere in its scope as $theNode, and can be passed as parameter when applying or calling templates.

Upvotes: 2

Tomalak
Tomalak

Reputation: 338406

No, this is not possible with vanilla XSLT 1.0. There is no easy way to retrieve an XPath expression string for a given node, and there is definitely no way to evaluate a string that looks like XPath as if it was XPath.

There are extensions that support dynamic evaluation of XPath expressions, but these are not compatible with every XSLT processor.

In any case, if you provide more detail around what you are actually trying to do, there might be another way to do it.

Upvotes: 1

Related Questions