Find an item that has only one other type of child

I want to use XPath to search for each <blockquote> element that contains at least one <pre> child element, other types of child elements, and optionally text nodes as child elements:

 <body><div><!-- arbitrary nesting --> <blockquote><pre>YES</pre></blockquote> <blockquote><p>NO</p></blockquote> <blockquote><pre>NO</pre><p>NO</p></blockquote> <blockquote><p>NO</p><pre>NO</pre></blockquote> <blockquote><pre>YES</pre> <pre>YES</pre></blockquote> <blockquote>NO</blockquote> </div></body> 

This XPath works, but I suspect it is too complex:

 //blockquote[pre][not(*[not(name()="pre")])] 

Is there a better way (less code, more efficient, more DRY) to choose what I want?

+4
source share
2 answers

Using

 //blockquote[* and not(*[not(self::pre)])] 

This selects all blockquote elements in the XML document that have at least one child and do not have a child that is not a pre element.

It’s just applying the double negation law :).

Note that this expression is more efficient than one that takes into account all the child elements of the element (since the selection stops right when the pre child is found).

XSLT Based Validation :

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:strip-space elements="*"/> <xsl:template match="/"> <xsl:copy-of select="//blockquote[* and not(*[not(self::pre)])]"/> </xsl:template> </xsl:stylesheet> 

When this conversion is applied to the provided XML document:

 <body><div><!-- arbitrary nesting --> <blockquote><pre>YES</pre></blockquote> <blockquote><p>NO</p></blockquote> <blockquote><pre>NO</pre><p>NO</p></blockquote> <blockquote><p>NO</p><pre>NO</pre></blockquote> <blockquote><pre>YES</pre> <pre>YES</pre></blockquote> <blockquote>NO</blockquote> </div></body> 

an XPath expression is evaluated, and the selected nodes are copied to the output:

 <blockquote> <pre>YES</pre> </blockquote> <blockquote> <pre>YES</pre> <pre>YES</pre> </blockquote> 
+2
source
 //blockquote[pre][count(pre)=count(*)] 
+5
source

All Articles