3

I am attempting to apply an XSLT transformation to a batch of XML documents. The main point of the transform is to re-order several elements. I wish to preserve any comments that directly precede an element:

<!-- not this comment -->
<element />

<!-- this comment -->
<!-- and this one -->
<element />

The closest I've come to a solution is to use the expression:

<xsl:template match="element">
    <xsl:copy-of select="preceding-sibling::comment()"/>
</xsl:template>

which grabs too many comments:

<!-- not this comment -->
<!-- this comment -->
<!-- and this one -->

I understand why the aforementioned XPath does not work correctly, but I don't have any good ideas on how to proceed. What I'm looking for is something to select all preceding comments whose following-sibling is either another comment or the current element being processed:

preceding-sibling::comment()[following-sibling::reference_to_current_element() or following-sibling::comment()]
Josh Johnson
  • 8,832
  • 4
  • 25
  • 31

2 Answers2

5
<xsl:template match="element">
  <xsl:copy-of select="preceding-sibling::comment()[
    generate-id(following-sibling::*[1]) = generate-id(current())
  ]"/>
</xsl:template>

More efficient:

<xsl:key 
  name  = "kPrecedingComment" 
  match = "comment()" 
  use   = "generate-id(following-sibling::*[1])" 
/>

<!-- ... -->

<xsl:template match="element">
  <xsl:copy-of select="key('kPrecedingComment', generate-id())" />
</xsl:template>
Lukas Eder
  • 211,314
  • 129
  • 689
  • 1,509
Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • Thanks, your solution works perfectly! I'll have to do some reading on generate-id() and key(). – Josh Johnson Jun 07 '10 at 16:36
  • @Joshua Johnson: Regarding keys: I've written up something that explains how they work on the example of JavaScript, take a look if you want: http://stackoverflow.com/questions/948218/xslt-3-level-grouping-on-attributes/955527#955527 (just read the lower part of the answer). – Tomalak Jun 07 '10 at 16:49
1

I think the best way of putting it would be as follows:

I want the preciding comments but only those with the current node as the first element to follow them.

Then, in xpath1.0/xslt1.0:

<xsl:template match="element">
<xsl:copy-of select="preceding-sibling::comment()[count(following-sibling::*[1]|current()) = 1]"/>
</xsl:template>
  • 1
    @Tomalak: You're right. Sorry for that! Every node in a node-set is disctint (there is not duplicate "node reference" in a node-set). So, to check that two nodes are in fact the same node, you don't need the generate-id() function (wich garantee a unique id string for a node into the same transformation process). You only need to check that the union produce a node-set with one node. It's a reduce for de identity node-set expression: "count($a | $b) = count($b) and count($b) = count($a)". Sorry for translation, my lenguage is spanish. –  Jun 07 '10 at 17:21