Search code examples
phpxmldomdocumentgetelementsbytagnamechild-nodes

PHP XML child parsing issue


This is driving me nuts. In my XML file, I have nested children of the same name <entry> and I am trying to get the top level only. If I call getElementsByTagName() it grabs all of them, so I am parsing for direct children and nothing seems to be working correctly.

<locations>
  <devices>
    <entry>
      <a/>
      <b/>
      <c>
        <entry>
        ..
        </entry>
      </c>
    </entry>
    <entry>
      <a/>
      <b/>
      <c>
        <entry>
        ..
        </entry>
      </c>
    </entry>
  </devices>
</locations>

<?
$path = "Export.txt" ;
$xml = file_get_contents( $path );
$dom = new DOMDocument( '1.0', 'utf-8' );
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;

// use it as a source
$dom->loadXML($xml) ;

// grab all "devices" should ONLY be 1 device
$devices = $dom->getElementsByTagName('devices');

$entries = array() ;
// parse through each FIRST child...which should be the first level <entry>
// however, the below is empty.
for ($i = 0; $i < $devices->childNodes->length; ++$i) {
    echo $count++ ;
    $entries[] = $devices->childNodes->item($i);
}

// but I get the following error on this foreach:
// Warning: Invalid argument supplied for foreach() in process.php
foreach ($devices->childNodes as $node) {
    echo "This: " . $count++ ;
}

// this prints "1": which is correct.
echo sizeof($devices) ;

//extra question concerning extracting getElementsByTag from a childNode

foreach ($devices as $device) { 
  foreach($device->childNodes as $child) { // this should be each parent <entry>
    $thisC = $child->getElementsByTagName('c') ;  // this should be only <c> tags BUT THIS NEVER SEEMS TO WORK
    foreach ($thisC->childNodes as $subEntry) {
      echo $subEntry->nodeValue ;
    }
  }
}

Solution

  • You can use an XPath query to get the relevant elements:

    <?php
    $dom = new DomDocument("1.0", "utf-8");
    $dom->loadXML(file_get_contents("export.txt"));
    $xpath = new DomXPath($dom);
    $entries = $xpath->query("/locations/devices/entry");
    $count = 0;
    // $entries is a DomNodeList
    var_dump($entries);
    foreach ($entries as $entry) {
        //do stuff with $entry
    }
    

    Or, to use your original approach:

    <?php
    $dom = new DomDocument("1.0", "utf-8");
    $dom->loadXML(file_get_contents("export.txt"));
    $devices = $dom->getElementsByTagName('devices');
    $entries = [];
    foreach ($devices as $device) {
        foreach ($device->childNodes as $child) {
            if ($child instanceof DomElement && $child->tagName === "entry") {
                $entries[] = $child;
            }
        }
    }
    // $entries is an array of DomElement
    var_dump($entries);
    foreach ($entries as $entry) {
        //do stuff with $entry
    }