given this xml:
<root>
<list>
<!-- foo's comment -->
<item name="foo" />
<item name="bar" />
<!-- another foo's comment -->
<item name="another foo" />
</list>
</root>
I'd like to use a XPath to select all item-nodes that have a comment immediately preceding them, that is I like to select the "foo" and "another foo" items, but not the "bar" item.
I already fiddled about the preceding-sibling axis and the comment() function but to no avail.
This seems to work:
//comment()/following-sibling::*[1]/self::item
It looks for immediately following siblings of comments which are also <item>
elements. I don't know a better way to express the ::*[1]/self::item
part, which is ugly; note that if it were written ::item[1]
then it would also find <item>
s not immediately proceded by a comment.
The currently selected solution:
//comment()/following-sibling::*[1]/self::item
doesn't work in the case where there is a procesing instruction (or a whole group of processing instructions) between the comment and the element -- as noticed in a comment by Martin Honnen.
The solution below doesn't have such a problem.
The following XPath expression selects only elements nodes that are either immediately preceded by a comment node, or are immediately preceded by a white-space-only text node, which is immediately preceded by a comment node:
(//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 a complete test:
We use this XML document:
<root>
<list>
<!-- foo's comment -->
<item name="foo" />
<item name="bar" />
<!-- another foo's comment -->
<item name="another foo" />
<!-- comment 3 --><item name="immed.after 3"/>
<!-- comment 4 --><?PI ?><item name="after PI"/>
</list>
</root>
When the following transformation is applied on 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>
the wanted, correct result is produced:
<item name="foo"/>
<item name="another foo"/>
<item name="immed.after 3"/>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With