4

Here's the XML code i'm working with:

<inventory>
        <drink>
                <lemonade supplier="mother" id="1">
                        <title>Super Lemonade</title>
                        <price>$2.50</price>
                        <amount>20</amount>
                </lemonade>
                <lemonade supplier="mike" id="4">
                        <title>Lemonade Plus</title>
                        <price>$3.00</price>
                        <amount>20</amount>
                </lemonade>
                <pop supplier="store" id="2">
                        <title>Poppys</title>
                        <price>$1.50</price>
                        <amount>10</amount>
                </pop>
        </drink>
</inventory>

Then i wrote a simple code to practice working with XPath:

<?php
        $xmldoc = new DOMDocument();
        $xmldoc->load('sample.xml');

        $xpathvar = new Domxpath($xmldoc);

        $queryResult = $xpathvar->query('//lemonade/price');
        foreach($queryResult as $result){
                echo $result->textContent;
        }
?>

That code is working well, outputting all the lemonade price values as expected.

Now I need a XPATH to get me the TITLE and SUPPLIER of all items with amount = 20.

How can I do it ?

Thank you

FlamingMoe
  • 2,709
  • 5
  • 39
  • 64

2 Answers2

4

In general you want to use direct paths over // search because it's faster:

/inventory/drink/lemonade[amount=20]/title

and

/inventory/drink/lemonade[amount=20]/@supplier

You can combine both XPath queries with the | (UNION) operator, which will then return four nodes.

Programmatic access to attributes with DOM is explained in:

You can find a good XPath tutorial at

Community
  • 1
  • 1
Gordon
  • 312,688
  • 75
  • 539
  • 559
  • But joining with | just give me the 2 values of title, and 2 values of supplier ... I would like 2 "arrays" with title+supplier together! – FlamingMoe Feb 15 '12 at 14:11
1

Use:

/*/*/*[amount = 20]/@supplier | /*/*/*[amount = 20]/title 

This selects:

  1. Any supplier attribute of any element that is a grand-child of the top element of the XML document (the same as /inventory/drink/lemonade, but I like to write shorter XPath expressions), whose amount child has a string value that is castable to the number 20.

  2. Any title child of any element that is a grand-child of the top element of the XML document (the same as /inventory/drink/lemonade, but I like to write shorter XPath expressions), whose amount child has a string value that is castable to the number 20.

XSLT - based verification:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:template match="node()|@*">
     <xsl:for-each select=
     "/*/*/*[amount = 20]/@supplier
    |
      /*/*/*[amount = 20]/title">

      <xsl:value-of select="."/>
      <xsl:text>&#xA;</xsl:text>
     </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided XML document:

<inventory>
    <drink>
        <lemonade supplier="mother" id="1">
            <title>Super Lemonade</title>
            <price>$2.50</price>
            <amount>20</amount>
        </lemonade>
        <lemonade supplier="mike" id="4">
            <title>Lemonade Plus</title>
            <price>$3.00</price>
            <amount>20</amount>
        </lemonade>
        <pop supplier="store" id="2">
            <title>Poppys</title>
            <price>$1.50</price>
            <amount>10</amount>
        </pop>
    </drink>
</inventory>

it evaluates the XPath expression and copies the values of the selected modes on separate lines:

mother
Super Lemonade
mike
Lemonade Plus

Do note:

Your learning of XPath can greatly be helped by using a tool such as the XPath Visualizer -- used by many thousands of people to learn XPath the fun way.

Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
  • But joining with | just give me the 2 values of title, and 2 values of supplier ... I would like 2 "arrays" with title+supplier together! – FlamingMoe Feb 15 '12 at 14:11
  • @user311188: Evaluating a single XPath expression always produces at most one node-set. If you want to get two different node-sets, you have to evaluate two different XPath expressions -- one for each of the two nodesets that you need. Also, in your question you do not mention such a requirement. – Dimitre Novatchev Feb 15 '12 at 14:14
  • Dimitre ... what if one expresion gives 5 results , and other 4 results ? how can i combine them ? – FlamingMoe Feb 15 '12 at 14:16
  • @user311188: Depends on what you mean by "combine". In XPath one can combine expressions using different operators: the union operator `|`, using the `or` and `and` operators and the `not()` function, ... etc. In case these aren't sufficient, then one can process the node-sets that are selected by these XPath expression in the programming language that is hosting XPath (e.g. XSLT, XQuery, C#, Java, ...). – Dimitre Novatchev Feb 15 '12 at 14:23
  • Dimitre... I can't get a solution :( I'll show you my real example: http://pastebin.com/vYQ1i7Kv it's a Google Docs available documents XML, joining folders and documents and the tree of them. What I need is a result giving me the tree of folders. Can you help me please ? I have this: /*/*/d:category[ contains(@label,"folder") ]/../d:title | /*/*/d:category[ contains(@label,"folder") ]/../d:link[ contains(@rel,"parent") ]/@title but this combine is what I need in a single array ... – FlamingMoe Feb 15 '12 at 14:45
  • @user311188: I'd recommend that you ask a new question and present all relevant/necessary data there. When you have done this, please, let me know via a comment and I'll have a look. – Dimitre Novatchev Feb 15 '12 at 15:28