XPath 1.0 check count

count, nodes, xml, xpath

Solution

Use:

/*/book[(.//image)[2]]

This selects all `book` elements that are children of the top element of the XML document and that have a second `image` descendant.

This expression is evaluated potentially faster than any expression starting with `//`, because an expression starting with `//` typically causes the whole document to be traversed.

It is also more efficient than:

//book[count(.//image)>1] 

even if this expression was re-written not to start with `//`.

This is so, because in the above expression `count(.//image)` causes all `image` descendants to be counted, while in our solution:

(.//image)[2]

only verifies that a second `image` descendant exists.

Finally, here is an XSLT - based verification:

<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="node()|@*">
     <xsl:copy-of select="/*/book[(.//image)[2]]"/>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied to the provided XML document:

<inventory>
        <book num="b1">
            <title>Snow Crash</title>
            <author>Neal Stephenson</author>
            <publisher>Spectra</publisher>
            <price>14.95</price>
            <chapter>
                <title>Snow Crash - Chapter A</title>
                <paragraph>
                    This is the <emph>first</emph> paragraph.
                    <image file="firstParagraphImage.gif"/>
                    afetr image...
                </paragraph>
                <paragraph>
                    This is the <emph>second</emph> paragraph.
                    <image file="secondParagraphImage.gif"/>
                    afetr image...
                </paragraph>
            </chapter>
            <chapter>
                <title>Snow Crash - Chapter B</title>
                <section>
                    <title>Chapter B - section 1</title>
                    <paragraph>
                        This is the <emph>first</emph> paragraph of section 1 in chapter B.
                        <image file="Chapter_B_firstParagraphImage.gif"/>
                        afetr image...
                    </paragraph>
                    <paragraph>
                        This is the <emph>second</emph> paragraph of section 1 in chapter B.
                        <image file="Chapter_B_secondParagraphImage.gif"/>
                        afetr image...
                    </paragraph>
                </section>
            </chapter>
            <chapter>
                <title>Chapter C</title>
                <paragraph>
                    This chapter has no images and only one paragraph
                </paragraph>
            </chapter>
        </book>
        <book num="b2">
            <title>Burning Tower</title>
            <author>Larry Niven</author>
            <author>Jerry Pournelle</author>
            <publisher>Pocket</publisher>
            <price>5.99</price>
            <chapter>
                <title>Burning Tower - Chapter A</title>
            </chapter>
            <chapter>
                <title>Burning Tower - Chapter B</title>
                <paragraph>
                    This is the <emph>second</emph> paragraph of chapter B in the 2nd book.
                    <image file="Burning_Tower_Chapter_B_secondParagraphImage.gif"/>
                    afetr image...
                </paragraph>
            </chapter>
        </book>
        <book num="b3">
            <title>Zodiac</title>
            <author>Neal Stephenson</author>
            <publisher>Spectra</publisher>
            <price>7.50</price>
            <chapter>
                <title>Zodiac - Chapter A</title>
            </chapter>
        </book>
        <!-- more books... -->
</inventory>

the XPath expression is evaluated and the selected nodes (in this case just one) are copied to the output:

<book num="b1">
   <title>Snow Crash</title>
   <author>Neal Stephenson</author>
   <publisher>Spectra</publisher>
   <price>14.95</price>
   <chapter>
      <title>Snow Crash - Chapter A</title>
      <paragraph>
                    This is the <emph>first</emph> paragraph.
                    <image file="firstParagraphImage.gif"/>
                    afetr image...
                </paragraph>
      <paragraph>
                    This is the <emph>second</emph> paragraph.
                    <image file="secondParagraphImage.gif"/>
                    afetr image...
                </paragraph>
   </chapter>
   <chapter>
      <title>Snow Crash - Chapter B</title>
      <section>
         <title>Chapter B - section 1</title>
         <paragraph>
                        This is the <emph>first</emph> paragraph of section 1 in chapter B.
                        <image file="Chapter_B_firstParagraphImage.gif"/>
                        afetr image...
                    </paragraph>
         <paragraph>
                        This is the <emph>second</emph> paragraph of section 1 in chapter B.
                        <image file="Chapter_B_secondParagraphImage.gif"/>
                        afetr image...
                    </paragraph>
      </section>
   </chapter>
   <chapter>
      <title>Chapter C</title>
      <paragraph>
                    This chapter has no images and only one paragraph
                </paragraph>
   </chapter>
</book>

Problem

I have the following XML document : ``` <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE inventory SYSTEM "books.dtd"> <inventory> <book num="b1"> <title>Snow Crash</title> <author>Neal Stephenson</author> <publisher>Spectra</publisher> <price>14.95</price> <chapter> <title>Snow Crash - Chapter A</title> <paragraph> This is the <emph>first</emph> paragraph. <image file="firstParagraphImage.gif"/> afetr image... </paragraph> <paragraph> This is the <emph>second</emph> paragraph. <image file="secondParagraphImage.gif"/> afetr image... </paragraph> </chapter> <chapter> <title>Snow Crash - Chapter B</title> <section> <title>Chapter B - section 1</title> <paragraph> This is the <emph>first</emph> paragraph of section 1 in chapter B. <image file="Chapter_B_firstParagraphImage.gif"/> afetr image... </paragraph> <paragraph> This is the <emph>second</emph> paragraph of section 1 in chapter B. <image file="Chapter_B_secondParagraphImage.gif"/> afetr image... </paragraph> </section> </chapter> <chapter> <title>Chapter C</title> <paragraph> This chapter has no images and only one paragraph </paragraph> </chapter> </book> <book num="b2"> <title>Burning Tower</title> <author>Larry Niven</author> <author>Jerry Pournelle</author> <publisher>Pocket</publisher> <price>5.99</price> <chapter> <title>Burning Tower - Chapter A</title> </chapter> <chapter> <title>Burning Tower - Chapter B</title> <paragraph> This is the <emph>second</emph> paragraph of chapter B in the 2nd book. <image file="Burning_Tower_Chapter_B_secondParagraphImage.gif"/> afetr image... </paragraph> </chapter> </book> <book num="b3"> <title>Zodiac</title> <author>Neal Stephenson</author> <publisher>Spectra</publisher> <price>7.50</price> <chapter> <title>Zodiac - Chapter A</title> </chapter> </book> <!-- more books... --> </inventory> ``` How to write an XPath 1.0 expression to select all books that have more then 1 image? I tried `inventory/book//image[2]/ancestor::book` but it give wrong result ... is `inventory/book//image[2]` give all the 2nd image in every book ?

Original source