1

Given a node list and a current node within that list, is there a way to use javax.xml.xpath (or org.apache.xpath perhaps) to evaluate position dependent XPath expressions, for example:

position()=last()-1

javax.xml.xpath contains:

evaluate(String expression, Object item, QName returnType) Evaluate an XPath expression in the specified context and return the result as the specified type.

I guess I could create a temporary node, add the nodes in the node list as children, and pass the child corresponding to the current node to evaluate as the context, but (assuming that even works) is there a better way?

Alternatively, in XSLT 1.0, suppose I have these three things (the node list, the node, and the xpath expression (as a string) ) as variables. Is it possible to apply this expression to the node, using the node list as context, and get the result as a variable?

JasonPlutext
  • 15,352
  • 4
  • 44
  • 84

3 Answers3

3

Most XPath APIs, and certainly the JAXP API, only allow you to set a singleton focus, that is, a focus in which you can choose any item as the context item, but the context position and size are fixed at 1.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164
1

Xalan Java supports http://www.exslt.org/dyn/functions/evaluate/index.html so with that you should be able to use e.g.

<xsl:variable name="nodes" select="/root/foo/bar"/>
<xsl:variable name="expression" select="concat('$nodes/', $yourPath)"/>
<xsl:variable name="result" select="dyn:evaluate($expression)"/>

(with xmlns:dyn="http://exslt.org/dynamic" defined of course).

Does that help? I am not sure I understand the section of having a node list and a single node and wanting to use both as a context.

As for the Java side and finding the node before the last one in a DOM NodeList, shouldn't nodeList.getItem(nodeList.getLength() - 2) suffice?

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
0

This post from 7 years ago suggests a Xalan specific non-XSLT solution might be available, but implementation independence and Michael's response pushed me in the direction of looking for a working XSLT1-based solution.

Here it is; I'm not sure whether it'll be useful to anyone else. I wonder whether there is a simpler way?

You can rely on XSLT, provided you set everything up correctly. From the spec:

In XSLT, an outermost expression (i.e. an expression that is not part of another expression) gets its context as follows:

the context node comes from the current node

the context position comes from the position of the current node in the current node list; the first position is 1

Starting at the end, I get the result of applying expression $expression into a variable $result:

        <xsl:variable name="result" >
            <xsl:apply-templates select="$vNodeSet" mode="myeval">
                <xsl:with-param name="expression" ><xsl:value-of select="$expression"/></xsl:with-param>
                <xsl:with-param name="pos" ><xsl:value-of select="$pos"/></xsl:with-param>
            </xsl:apply-templates>
        </xsl:variable> 

That apply-templates pushes a suitable "current node list" to the following template:

  <xsl:template match="*" mode="myeval">
    <xsl:param name="expression">1. </xsl:param>
    <xsl:param name="pos">3</xsl:param>

    <xsl:choose>
        <xsl:when test="position()=$pos">
            <xsl:value-of select="dyn:evaluate($expression)" /></xsl:when>
        <xsl:otherwise /> 
    </xsl:choose>

  </xsl:template>

This template evaluates the expression for the node I want to be the "current node". Notice:

  1. the use of dy:evaluate (thanks Martin!)
  2. $pos which identifies what I want as the current node.

I was able to calculate $pos using Dimitre's answer to an earlier question; I've also used his variable name vNodeSet

Thanks Dimitre, Martin, and Michael!

Community
  • 1
  • 1
JasonPlutext
  • 15,352
  • 4
  • 44
  • 84
  • Jason, some comments: instead of `` simply use ``, that is shorter to write and more efficient. The same approach can be used for the second `xsl:with-param`. And I think if all you want to do is apply `dyn:evaluate` to a certain node in your node-set you only need `` plus ``. – Martin Honnen Jul 13 '12 at 10:33
  • Thanks Martin. I think I tried first, but that didn't work (using Xalan), though I might be mistaken. – JasonPlutext Jul 13 '12 at 12:43