tags:

views:

630

answers:

3

Hey,

I know there is the current() function to retrieve the current node in XSL, but is there a way to be able to reference the "previous" and "next" nodes in reference to the current position?

+1  A: 

Assuming you are talking about next and previous nodes in the document structure, as opposed to the current execution flow (such as a for-each loop), see the preceding-sibling and following-sibling axes: XPath Axes on W3.

To get the next node of the same name:

following-sibling::*[name() = name(current())]

Depending on the context, you may need to use name(.) in the first part.

sirlancelot
+4  A: 

No. The current context cannot know which nodes are "next" or "previous".

This is because when, for example, templates are applied, the mechanics go like this:

  1. You do: <xsl:apply-templates select="*" /><!-- select 3 nodes (a,b,c) -->
  2. The XSLT processor makes list of nodes to be processed (a,b,c)
  3. For each of those nodes, the XSLT processor selects and executes a matching template
  4. When the template is called, the current() node is defined, and position() is defined, but other than that the template has no knowledge of the execution flow.
  5. Execution order is subject to the processors preferences, as long as the outcome is guaranteed to be the same. Your (theoretical) predictions might be true for one processor, and wrong for another. For a side-effects free programming language like XSLT, knowledge like this would be a dangerous thing, I think (because people would start to rely on execution order).

You can do use the following::sibling or preceding::sibling XPath axes, but that's something different from knowing what node will be processed next

EDIT

The above explanation tries to answer the question as it has been asked, but the OP meant something different. It's about grouping/outputting unique nodes only.

As per the OP's request, here a quick demonstration of how to achieve a grouping using the XPath axes.

XML (the items are pre-sorted):

<items>
  <item type="a"></item>
  <item type="a"></item>
  <item type="a"></item>
  <item type="a"></item>
  <item type="b"></item>
  <item type="e"></item>
</items>

XSLT

<xsl:stylesheet 
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>

  <xsl:template match="/items">
    <!-- copy the root element -->
    <xsl:copy>
      <!-- select those items that differ from any of their predecessors -->
      <xsl:apply-templates select="
        item[
          not(@type = preceding-sibling::item/@type)
        ]
      " />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="item">
    <!-- copy the item to the output -->
    <xsl:copy-of select="." />
  </xsl:template>

</xsl:stylesheet>

Output:

<items>
  <item type="a"></item>
  <item type="b"></item>
  <item type="e"></item>
</items>
Tomalak
As in, I'm processing a large sorted list. Example<item type="a"></item><item type="a"></item><item type="a"></item><item type="a"></item><item type="b"></item><item type="e"></item>I want to be able to tell whether the previous item was the same type, so I am able to enact some CSS to "group" them.
James
This is best done through Muenchian grouping, though if the list of items is sorted you can get by with `"//item[@type != preceding-sibling::item/@type]"`
Tomalak
Would you be able to put this into some working code? I can't get it to work. So make it say HEY if the previous is same? Thanks!
James
Try to get your mind off the "previous/next" fixation. ;-) The above expression *selects the unique nodes* only, there is no need to care for the previous or next node. I'll try to demonstrate with your input snippet.
Tomalak
Thanks! My mind is too small to grasp these concepts just yet :) Demonstration greatly appreciated!
James
Note that `"//item[@type != preceding-sibling::item/@type]"` is actually the wrong thing, for reasons explained here: http://social.msdn.microsoft.com/forums/en-US/xmlandnetfx/thread/9a3fa413-2a2d-467c-a3a0-30c7f73f1ec3. Sorry for the confusion, my bad.
Tomalak
A: 

Hi,

You could track previous and current in variables for later handling. I.e. you can keep tag(i-2), tag(i-1) and work with them in tag(i).

Just another idea.

Regards.

ATorras