Alexander Farber
Alexander Farber

Reputation: 22988

Select all nodes with a certain name by using PHP SimpleXML - source code attached

In the following XML file I'm trying to print all TestItem nodes, but get only the 4 outer nodes.

Does anybody please know, how to print every node having that name, regardless of their position?

data.xml:

<?xml version="1.0"?>
<Tests>
    <TestItem Name="UpdateBootProfile" Result="PASS" />
    <TestItem Name="NRB Boot" Result="PASS">
      <TestItem Name="Boot Test" Result="PASS">
        <TestItem Name="PreparePowerSupply" Result="PASS" />
        <TestItem Name="ApplyBatteryVoltage" Result="PASS" />
        <TestItem Name="Shelf Mode test" Result="PASS">
        </TestItem>
        <TestItem Name="ApplyUSBVoltage" Result="PASS" />
        <TestItem Name="DetectBoard" Result="PASS" />
        <TestItem Name="Device Current Profile" Result="PASS" />
        <TestItem Name="Device Connection" Result="PASS">
        </TestItem>
      </TestItem>
    </TestItem>
    <TestItem Name="Check device Type" Result="PASS" />
    <TestItem Name="Assign BSN and Erase EFS" Result="PASS">
    </TestItem>
</Tests>

parse.php:

<?php
        $tmp = 'data.xml';
        $str = file_get_contents($tmp);
        $xml = new SimpleXMLElement($str);
        $items = $xml->xpath('TestItem');

        while(list( , $test) = each($items)) {
                printf("%s %s\n", $test['Name'], $test['Result']);
        }
?>

php -f parse.php output (why does it list only 4 TestItems?):

UpdateBootProfile PASS
NRB Boot PASS
Check device Type PASS
Assign BSN and Erase EFS PASS

Using PHP 5.3.5 on CentOS 6.3 command line.

UPDATE:

The suggested //TestItem works for my simple test case above, thank you.

But it still fails for my real data (which I can't paste here):

# grep -w TestItem my_real_file_May_2013_09_35_38.xml |wc -l
143

# php -f parse.php |wc -l
86

Does anybody please have an idea, which would //TestItem miss some nodes?

UPDATE 2:

Actually it works! The grep commando above counted more lines because of some </TestItem> closing tags :-)

Upvotes: 4

Views: 9406

Answers (3)

hek2mgl
hek2mgl

Reputation: 157967

Use the following xpath to select nodes regardless from their position in tree:

$items = $xml->xpath('//TestItem');

Or

$items = $xml->xpath('//TestItem/TestItem');

If you only need the leaf nodes.

Output (from second one):

UpdateBootProfile PASS
NRB Boot PASS
Boot Test PASS
PreparePowerSupply PASS
ApplyBatteryVoltage PASS
Shelf Mode test PASS
ApplyUSBVoltage PASS
DetectBoard PASS
Device Current Profile PASS
Device Connection PASS
Check device Type PASS
Assign BSN and Erase EFS PASS

Note the //. Learn more about in the W3schools XPath tutorial.

Upvotes: 2

Guerra
Guerra

Reputation: 2790

function printxml($xml,$deep = 4){
if($xml instanceOf SimpleXMLElement)
    $xml = (array)$xml;
    foreach($xml->TestItem as $t){
        if(is_array($t) && $deep > 0)
            printxml($t, $deep-1);
        else
            echo $t['Name'].' '.$t['Result'];
    }
}

Try it, just have to do counter to get the deep of xml, in your case 4 levels.

Upvotes: -1

Smile
Smile

Reputation: 2758

You can simply do this

$testitems = simplexml_load_file("testitem.xml");
if(count($testitems)):
    $result = $testitems->xpath("//TestItem");

    //echo "<pre>";print_r($result);die;
    foreach ($result as $item):
        echo "Name ".$item['Name'] ." and result ". $item['Result'];
        echo "<hr>";
    endforeach;
endif;

By doing above you will get all elements haveing <TestItem> elements.

Upvotes: 5

Related Questions