Xslt and xpath: match previous comments

given this xml:

<root> <list> <!-- foo comment --> <item name="foo" /> <item name="bar" /> <!-- another foo comment --> <item name="another foo" /> </list> </root> 

I would like to use XPath to select all node nodes that have a comment immediately preceding them, that is, I like to select the "foo" and "other foo" elements, but not the "bar" element.

I have already searched for the axis of the previous brother and the comment () function, but to no avail.

+7
xpath xslt
source share
3 answers

It works:

 //comment()/following-sibling::*[1]/self::item 

He immediately searches for the next siblings for comments, which are also elements of <item> . I don't know a better way to express the part ::*[1]/self::item , which is ugly; note that if it were written ::item[1] , it would also detect <item> , which was not immediately processed by the comment.

+3
source share

Currently selected solution :

 //comment()/following-sibling::*[1]/self::item 

does not work if there is a processing instruction (or a whole group of processing instructions) between the comment and the element, as noted in the comment of Martin Honnen.

The solution below does not have such a problem .

In the following XPath expression, only node nodes are selected that either immediately precede the node comment or immediately preceded only by a space with a node followed by the node comment immediately:

 (//comment() /following-sibling::node() [1] [self::text() and not(normalize-space()) ] /following-sibling::node() [1] [self::item] ) | (//comment() /following-sibling::node() [1] [self::item] ) 

Here is the full test :

We use this XML document:

 <root> <list> <!-- foo comment --> <item name="foo" /> <item name="bar" /> <!-- another foo comment --> <item name="another foo" /> <!-- comment 3 --><item name="immed.after 3"/> <!-- comment 4 --><?PI ?><item name="after PI"/> </list> </root> 

When the following conversion applies to the above XML document :

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:template match="/"> <xsl:copy-of select= " (//comment() /following-sibling::node() [1] [self::text() and not(normalize-space()) ] /following-sibling::node() [1] [self::item] ) | (//comment() /following-sibling::node() [1] [self::item] ) "/> </xsl:template> </xsl:stylesheet> 

required, the correct result is obtained :

 <item name="foo"/> <item name="another foo"/> <item name="immed.after 3"/> 
+3
source share

As mentioned in this thread , introducing the test ( <xsl:if test="..."></xsl:if> ) as:

previous-sibling :: comment ()

will only check if node has a previous sibling, comment.

If you want to know from previous siblings that are elements or comments whether the closest comment is, you can try:

 (preceding-sibling::*|preceding-sibling::comment())[1][self::comment()] # WRONG 

BUT: this will not work, because although β€œ [1] ” means first in the opposite direction for preceding-sibling , it does not mean that for expressions in parentheses it means first in ordering a document

You can try:

 (preceding-sibling::*|preceding-sibling::comment())[last()][self::comment()] 

or

 preceding-sibling::node()[self::*|self::comment()][1][self::comment()] 

For example:

 <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > <xsl:output omit-xml-declaration="no" indent="no"/> <xsl:template match="//item"> <xsl:if test="preceding-sibling::node()[self::*|self::comment()][1][self::comment()]"> <xsl:value-of select="./@name" /> </xsl:if> </xsl:template> </xsl:stylesheet> 

will only be displayed:

 foo another foo 

when entering:

 C:\Prog\xslt\preceding-sibling_comment> java -cp ..\saxonhe9-2-0-6j\saxon9he.jar net.sf.saxon.Transform -s:test.xml -xsl:t.xslt -o:res.xml 

from:

  • test.xml : your file appears in your question
  • t.xslt : xslt file above
  • res.xml : received converted file

Edit: since it does not take into account processing instructions, I left this answer as a Community Wiki.

0
source share

All Articles