4

(Note: This post has been edited to show specific use case. See bottom.)

I want to use the current node's value of position() inside an xpath expression (in which the context changes). Unfortunately, I don't see any simple way of doing this because current()/position() appears to always equal 1.

[I assume this is because current()/position() says "what is the position of node current() within the context of... the one-node set containing only current().]

To be specific, let's say I have a sequence of nodes $seq and I want to return the *n*th node in the sequence ($seq[n]) where n equals the current value of position() (that is, the value of position() prior to the statement being evaluated).

Clearly, $seq[position()] won't work, because that expands to seq[position() = position()], returning the entire sequence.

$seq[position(current())] is jibberish.

The only thing I could find that appears to maybe work is

for $pos in position() return $seq[$pos]

I assume there is a simpler way.

Edit It was requested that I give a more specific example of what I'm trying to do. I actually found the first person's answer sufficient to my needs, though not much simpler than what I had come up with myself (the "for $pos in position() return $correctOrder[$pos]" option).

Here is the actual situation: I have a set of "session" nodes, each of which has a @startTime attribute. I want to find which ones are out of order with respect to that @startTime attribute, or at least not in the proper place they would be if the input document's nodes were properly sorted by @startTime.

This is just one of many tests that are performed on each of the session nodes.

What I wanted to do was create a sequence that had the nodes in the correct order and compare the nth element in the document-ordered sequence with the nth-element in the correctly ordered sequence.

Something like this:

<xsl:template match="/">
<xsl:variable name="correctOrder" as="node()*">
<xsl:for-each select="session">
<xsl:sort select="@xs:dateTime(@startTime)"/>
<xsl:sequence select="."/>
</xsl:for-each>
</xsl:variable>

<xsl:for-each select="session">

<---- Lots of various tests ---->

<xsl:if test="current()/@startTime eq 
$correctOrder[<-here I want to put the value "position()" had prior to this <xsl:if> statement->]/@startTime"
<error description="Node out of order."/>
</xsl:if>

<--- Lots of other tests----->

</xsl:for-each>
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
David R
  • 994
  • 1
  • 11
  • 27
  • David R: `position()` is undefined just by itself. `position()` against *what*? Please, edit the question and make it less ambiguous and more clearly defined. A concrete example would be excellent. – Dimitre Novatchev Jan 28 '12 at 16:53
  • Dimitre, I have put the specific use case, but I do think the question asked "how to reference the value that position() had prior to the invocation of the given instruction?" captures what I was looking for. That is why we have "current()" (in contrast with "."), because we want to reference the current node (rather than the context node) in some xpath statement. I'm just asking about the equivalent idea for various other context variables that change in the midst of an xpath expression. – David R Jan 29 '12 at 12:48
  • David R: Yes, it is clear now. As for "saving" or "synchronizing" different positions, I have shown how to do this in my answer. – Dimitre Novatchev Jan 29 '12 at 15:29

2 Answers2

0

Well as you have tagged your question as XSLT 2.0 I would use a variable e.g.

<xsl:variable name="pos" as="xs:integer" select="position()"/>

and then use that variable e.g.

<xsl:sequence select="$seq[$pos]"/>
Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
  • for some reason I thought variables could only be declared at the top of templates, documents, and functions. I guess that only applies to parameters. – David R Jan 28 '12 at 15:20
  • Question, is there any reason why wouldn't work as well? – David R Jan 28 '12 at 15:22
  • @DavidR: The reason it "won't work" is that you haven't told us what you understand by position. Taking this into account, Martin's answer is probably wrong, because he isn't a telepath to poke inside your brain and get what you really want. – Dimitre Novatchev Jan 28 '12 at 16:55
  • David, my main suggestion is to use `xsl:variable` as shown, then to use that variable in whatever expression you want or need to. The `xsl:sequence` is just there to give an example of XSLT code that fits most purposes in XSLT 2.0, if `xsl:value-of` gives you what you want you can of course use it instead of `xsl:sequence`. – Martin Honnen Jan 28 '12 at 18:02
0

Note: I apologize that due to a bug in SO's code formatting, this code isn't indented.

Here is an example that illustrates a general pattern using the same position:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema">
 <xsl:output omit-xml-declaration="yes" indent="yes"/> 

 <xsl:variable name="vSeq1" select="/*/names/*/text()"/>
 <xsl:variable name="vSeq2" select="/*/values/*/text()"/>

 <xsl:template match="/">
  <xsl:sequence select=
   "for $i in 1 to count($vSeq1)
     return
        ($vSeq1[$i], '=', $vSeq2[$i], '&#xA;')

   "/>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the following XML document:

<t>
 <names> 
  <name>N1</name>
  <name>N2</name>
  <name>N3</name>
  <name>N4</name>
  <name>N5</name>
 </names>

 <values>
  <value>V1</value>
  <value>V2</value>
  <value>V3</value>
  <value>V4</value>
  <value>V5</value>
 </values>
</t>

the wanted, correct result is produced:

N1=V1
N2=V2
N3=V3
N4=V4
N5=V5
Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
  • Thanks, Dimitre. I've only recently begun using the power of xslt2 sequences. I think yesterday I passed a milestone of sorts in my understanding of xslt2...many more to go. – David R Jan 30 '12 at 06:56