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.
The currently selected solution:
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:
Here is a complete test:
We use this XML document:
When the following transformation is applied on the above XML document:
the wanted, correct result is produced:
As mentioned in this thread, introducing a test (
<xsl:if test="..."></xsl:if>
) like:preceding-sibling::comment()
would only tests whether the node has a preceding sibling that's a comment.
If you want to know, of the preceding siblings that are elements or comments, whether the nearest one is a comment, you could try:
You can try:
or
For instance:
would only display:
when typing:
with:
test.xml
: your file displayed in your questiont.xslt
: the xslt file aboveres.xml
: the resulting transformed fileEdit: since it doesn't take into account processing instructions, I left that answer as Community Wiki.
This seems to work:
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.