1

I have an XML file structured as follows:

<pictures>
    <picture>
        <title></title>
        <description></description>
        <facts>
            <date></date>
            <place>Unites States</place>
        </facts>
        <people>
            <person>John</person>
            <person>Sue</person>
        </people>
    </picture>
    <picture>
        <title></title>
        <description></description>
        <facts>
            <date></date>
            <place>Canada</place>
        </facts>
        <people>
            <person>Sue</person>
            <person>Jane</person>
        </people>
    </picture>
    <picture>
        <title></title>
        <description></description>
        <facts>
            <date></date>
            <place>Canada</place>
        </facts>
        <people>
            <person>John</person>
            <person>Joe</person>
            <person>Harry</person>
        </people>
    </picture>
<pictures>

In one case, I need to search for pictures where place="Canada". I have an XPath that does this fine, as such:

$place = "Canada";
$pics = ($pictures->xpath("//*[place='$place']"));

This pulls the entire "picture" node, so I am able to display title, description, etc. I have another need to find all pictures where person = $person. I use the same type query as above:

$person = "John";
$pics = ($pictures->xpath("//*[person='$person']"));

In this case, the query apparently knows there are 2 pictures with John, but I don't get any of the values for the other nodes. I'm guessing it has something to do with the repeating child node, but can't figure out how to restructure the XPath to pull all of the picture node for each where I have a match on person. I tried using attributes instead of values (and modified the query accordingly), but got the same result. Can anyone advise what I'm missing here?

roybman
  • 25
  • 5

1 Answers1

3

Let's replace the variables first. That takes PHP out of the picture. The problem is just the proper XPath expression.

//*[place='Canada']

matches any element node that has a child element node place with the text content Canada.

This is the facts element node - not the picture.

Getting the pictures node is slightly different:

//picture[facts/place='Canada']

This would select ANY picture node at ANY DEPTH that matches the condition.

picture[facts/place='Canada']

Would return the same result with the provided XML, but is more specific and matches only picture element nodes that are children of the document element.

Now validating the people node is about the same:

picture[people/person="John"]

You can even combine the two conditions:

picture[facts/place="Canada" and people/person="John"]

Here is a small demo:

$element = new SimpleXMLElement($xml);

$expressions = [
  '//*[place="Canada"]',
  '//picture[facts/place="Canada"]',
  'picture[facts/place="Canada"]',
  'picture[people/person="John"]',
  'picture[facts/place="Canada" and people/person="John"]',
];

foreach ($expressions as $expression) {
  echo $expression, "\n", str_repeat('-', 60), "\n";
  foreach ($element->xpath($expression) as $index => $found) {
     echo '#', $index, "\n", $found->asXml(), "\n";
  }
  echo "\n";
}

HINT: Your using dyamic values in you XPath expressions. String literals in XPath 1.0 do not support any kind of escaping. A quote in the variable can break you expression. See this answer.

Community
  • 1
  • 1
ThW
  • 19,120
  • 3
  • 22
  • 44
  • Thanks. I still haven't entirely resolved my issue. – roybman Mar 06 '15 at 16:04
  • Sorry, hit enter by mistake. I still haven't entirely resolved my issue. I tried running this with a variable and with a string. When I run it as $xmlpics = ($pictures->xpath('picture[people/person="John"]')); the query works fine. When I put the variable back in: $xmlpics = ($pictures->xpath('picture[people/person="$person"]')); it doesn't work. I have verified multiple times, including echo of everything, that $person = "John", and there are no special characters. I can't figure this part out. Makes no sense. – roybman Mar 06 '15 at 16:11
  • I got it. It's a matter of reversing the single and double quotes. When run as - $xmlpics = ($pictures->xpath("picture[people/person='$person']")); - this works. Thanks very much. – roybman Mar 06 '15 at 16:26
  • The quotes are exchangeable. Both PHP and XPath support single and double quotes for string literals. But XPath has no escaping, so quotes in the variable value can break your XPath expression. See the answer linked in the hint for more infos. – ThW Mar 06 '15 at 16:44