URL87
URL87

Reputation: 11012

XPath 1.0 check count

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 ?

Upvotes: 4

Views: 3568

Answers (3)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243449

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>

Upvotes: 5

dirkgently
dirkgently

Reputation: 111130

inventory/book//image[2]/ gets you the second image provided there are 2 or more image child nodes in your book element. Try:

   //inventory/book[count(descendant::image) > 1]

You need to move down the hierarchy to a book element and then start your query from there on. The predicate (or query in layman terms) is to look for all image elements from there on -- which is exactly what the descendant axis does. You append :: and nodename to select a particular descendant as we do with descendant::image to look for all images. The final test if to make sure if the count (as the name of the function suggests) is greater than 1 or not.

Upvotes: 3

neu-rah
neu-rah

Reputation: 1693

try with this

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

all books that have more than one image tag somewhere

Upvotes: 4

Related Questions