xslt & xpath: match directly preceding comments

2020-04-10 01:22发布

问题:

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()]

回答1:

<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>


回答2:

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>


标签: xml xslt xpath