0

I am stock with this XML problem, I have a XML file that I browse to find values. Everything is working fine, I can read on all the child nodes, but I am stuck on this section. The XML portion containing photos are all the same name of the node, except for an attribute, how can I specify how to browse according to this and take the filename value of each of them

XML 
...
<Engine>
            <Fuel>Unleaded</Fuel>
            <Cylinders>4</Cylinders>
            <Induction>Normally aspirated</Induction>
          </Engine>
          <Photo order="1">
            <Filename>http://usedcarpics.s3.amazonaws.com/514SPINELLITOYOTA2/b5092588_2.jpg</Filename>
          </Photo>
          <Photo order="2">
            <Filename>http://usedcarpics.s3.amazonaws.com/514SPINELLITOYOTA2/b5092588_3.jpg</Filename>
          </Photo>
          <Photo order="3">
            <Filename>http://usedcarpics.s3.amazonaws.com/514SPINELLITOYOTA2/b5092588_4.jpg</Filename>
          </Photo>
          <Photo order="4">
            <Filename>http://usedcarpics.s3.amazonaws.com/514SPINELLITOYOTA2/b5092588_5.jpg</Filename>
          </Photo>
          <Photo order="5">
            <Filename>http://usedcarpics.s3.amazonaws.com/514SPINELLITOYOTA2/b5092588_6.jpg</Filename>

...

In my php file, I have this code that help me find the values:

$import->stock_no =(string)$item->Invoice->Vehicle->VehicleStock;           

$import->image1 =(string)$item->Invoice->Vehicle->Photo->attributes(order="1")->Filename; 

Of course it doesn't work, how can I browse all the photo nodes( I have 8 pictures I need to take the values from)

I want to have $import->image1 = (filename in the attibutes of pohoto 1), sames for image 2, 3, etc.

Thank you.

maphaneuf
  • 77
  • 6
  • 1
    `attributes(order="1")` is not exactly valid PHP anyways... – Marc B Jul 08 '13 at 18:13
  • I understand that, but how can I select photo with attribute called order="1" than order="2", ... until I reach order ="8" – maphaneuf Jul 08 '13 at 18:19
  • For example using XPATH or just a loop over elements. – str Jul 08 '13 at 18:21
  • looping thru, would be something like this: $import->image1 =(string)$item->Invoice->Vehicle->Photo->Filename; $import->image2 =(string)$item->Invoice->Vehicle->Photo->Filename; $import->image3 =(string)$item->Invoice->Vehicle->Photo->Filename; $import->image4 =(string)$item->Invoice->Vehicle->Photo->Filename; – maphaneuf Jul 08 '13 at 18:24
  • No, a loop is either `foreach`, `while`, or `for`. – str Jul 08 '13 at 18:27
  • [XPath](http://nl1.php.net/manual/en/simplexmlelement.xpath.php) is your friend, `//Photo[@order="1"]/Filename`, or if you want them all loop through `//Photo[@order]/Filename`.. – Wrikken Jul 08 '13 at 18:30
  • I do not understand. Ok for foareach, this I tried and it's not working for me. But Xpath, how do I write this, $import->image1 = //Photo[@order="1"]/fimename ? – maphaneuf Jul 08 '13 at 18:38

3 Answers3

1

What you try to achieve is (first of all) possible by using an xpath query. You want to access a child-node based on an attribute value. The better reference questions in SimpleXML are:

It's also since some days when the suggestion was given to extend form SimpleXMLElement to provide a utility function to actually do that with an easy interface:

However your case is a little different because of the syntax you suggest:

$xml = simplexml_load_string($buffer, 'MySimpleXMLElement');

echo $xml->Vehicle->Photo->attribute("order", "1")->Filename;

// prints "http://usedcarpics.s3.amazonaws.com/514SPINELLITOYOTA2/b5092588_2.jpg"

Instead of using an ordinary SimpleXMLElement this example uses an extended one named (exemplary) MySimpleXMLElement. It runs an XPath query inside based on the input parameters and based on the parent element it operates on (here being a Photo element):

/**
 * Class MySimpleXMLElement
 *
 * Example of how to magically access named child-nodes based
 * on an attribute value of theirs.
 */
class MySimpleXMLElement extends SimpleXMLElement
{
    public function attribute($name, $value) {

        $nodes = $this->xpath(
            sprintf('../%s[@%s = "%s"]', $this->getName(), $name, $value)
        );

        return $nodes ? $nodes[0] : NULL;
    }
}

This new MySimpleXMLElement::attribute() method (sorry attributes() was already in use) is then available on every node. So have fun.

Naturally you can also write it this way:

$xml = simplexml_load_string($buffer);

echo $xml->Vehicle->xpath('Photo[@order="1"]')[0]->Filename;

// prints "http://usedcarpics.s3.amazonaws.com/514SPINELLITOYOTA2/b5092588_2.jpg"

the extended SimpleXMLElement is mainly for convenience reasons. And it's probably more easy to debug in case you're not fluent with Xpath yet.

Last time I extended SimpleXMLElement on Stackoverflow was in the said answer to the "simplexml_load_file - redundant element with empty value is converted to new SimpleXMLElement Object" question.

Community
  • 1
  • 1
hakre
  • 193,403
  • 52
  • 435
  • 836
  • I get an error if I write it like you did. unexpected '[', expecting ',' or ';' – maphaneuf Jul 08 '13 at 20:27
  • I assume for the last example? If so, that is because you're probably using PHP 5.3 or 5.2 which are end of live. Upgrade to PHP 5.4 minimum *or* change to older array-access with the help of an additional function or variable. `$nodes = $xml->Vehicle->xpath('Photo[@order="1"]'); echo $nodes[0]->Filename;` - Also take care that I use the shortened example XML, so you paths to the elements are likely to be different then I have in the answer – hakre Jul 08 '13 at 20:40
  • Here is an online demo: https://eval.in/36953 - It also shows at the very end that - as the images are already ordered - you can also access them by their zero-based index with square brackets. – hakre Jul 08 '13 at 20:43
  • I have my code working, I used: echo $xml->Vehicle->Photo[0]->Filename, "\n"; and I get the image just like I wanted. Thank you very much. – maphaneuf Jul 09 '13 at 15:46
  • Yes, that exactly what I had in there as the last example. Sometimes it's easier than you first think. – hakre Jul 09 '13 at 16:30
0

Try this

    <?php
    $xml = '<Engine>
                <Fuel>Unleaded</Fuel>
                <Cylinders>4</Cylinders>
                <Induction>Normally aspirated</Induction>
            </Engine>
            <Photo order="1">
                <Filename>http://usedcarpics.s3.amazonaws.com/514SPINELLITOYOTA2/b5092588_2.jpg</Filename>
              </Photo>
              <Photo order="2">
                <Filename>http://usedcarpics.s3.amazonaws.com/514SPINELLITOYOTA2/b5092588_3.jpg</Filename>
              </Photo>';

    $xml="<Wraper>".$xml."</Wraper>";
    $parse=new SimpleXMLElement($xml);

    echo "Engine Fuel:".$parse->Engine->Fuel;
    echo "<br/>Engine Cylinders:".$parse->Engine->Cylinders;
    echo "Photos<br/>";
    foreach ($parse->Photo as $photo)
    {

        echo "<br/>Photo Order: ".$photo->attributes();
        echo "<br/>Photo URL: ".$photo->Filename;
        echo "<hr/>";
    }
    ?>
Hearaman
  • 8,466
  • 13
  • 41
  • 58
0

sweet and simple with xpath:

$xml = simplexml_load_string($x); // assume XML in $x

$photos = $xml->xpath("//Photo"); // select all Photo nodes and their children in an array

foreach ($photos as $photo)
    echo "order: $photo[order], file: $photo->Filename<br />"; // simple output

see it working: http://3v4l.org/SJmEg

michi
  • 6,565
  • 4
  • 33
  • 56
  • @maphaneuf click the blue `eval` button below the code, then see the generated output below that. – michi Jul 08 '13 at 20:29
  • almost, I had to change project, will return to this this afternoon. – maphaneuf Jul 09 '13 at 15:27
  • I actually have a working solution, I tryed this morning and It works. Here is what I have working, solution provided by hakre – maphaneuf Jul 09 '13 at 15:44
  • https://eval.in/36953 I just used the echo $xml->Vehicle->Photo[0]->Filename, "\n"; and it works because my iamge are all already in the right order. THank you to all – maphaneuf Jul 09 '13 at 15:45