Search code examples
phpxmlparsingxmlreader

Get Information of nodes in context with PHP XML Reader


@those who marked this question as duplicate, please leave a comment why you think this is. Here, we want to get all elements in a certain node. In the other Question they want to get the parent node. This is almost the opposite. Here we are not selecting the people node and its children but rather get ever person within a context. I see it might look similar.

Question: To be clear: I want to do this with XML Reader.

I have got an XML-file and want to retrieve the content of the name-node within the context people. The name-node also appears in cars, that's why I need the context.

How can I do this?

<file>
  <people>
     <person>
        <name>father</name>
        <age>50</age>
     </person>
  </people>
  <car>
     <person>
        <name>mom</name>
        <age>45</age>
     </person>
     <name>Octavia</name>
     <brand>Scoda</brand>
  </car>
  <people>
     <person>
        <name>son</name>
        <age>25</age>
     </person>
     <person>
        <name>daughter</name>
        <age>20</age>
     </person>
  </people>

Here is the PHP I got so far:

$xmlReader = new XMLReader;

if (!$xmlReader->open($this->_file, null, 1<<19)) {
   die("Failed to open file");
}


while($xmlReader->read()) {
    // set the context
    if ($xmlReader->nodeType == XMLReader::ELEMENT && $xmlReader->name == 'people') {
          // do something with the name of every person
          if ($xmlReader->nodeType == XMLReader::ELEMENT && $xmlReader->name == 'person') {
              $xmlReader->name->readString();
          }
    }
}

Solution

  • This is code:

    $xmlReader = new XMLReader;
    
    if (!$xmlReader->open($fileURL, null, 1<<19)) {
       die("Failed to open file");
    }
    while($xmlReader->read()) {
        if ($xmlReader->nodeType == XMLReader::ELEMENT && $xmlReader->name == 'people') {
    // We are at the beginning of the node people. 
            while ($xmlReader->read()) {
                if ($xmlReader->nodeType == XMLReader::ELEMENT && $xmlReader->name == 'person') {
    // We are at the beginning of the node person. 
                    while ($xmlReader->read()) {
                        if ($xmlReader->nodeType == XMLReader::ELEMENT && $xmlReader->name == 'name' && $xmlReader->read())
    // And now node 'name. Then we make one read more to step to 'TEXT' type of node. Because 'ELEMENT' does not have value         
                            echo $xmlReader->value.'<br>';
                        else if ($xmlReader->nodeType == XMLReader::END_ELEMENT && $xmlReader->name == "person")
    // This is closing element of node 'persons. Interrupt loop 
                            break;
                    }
                }
                if ($xmlReader->nodeType == XMLReader::END_ELEMENT && $xmlReader->name == "people")
    // This is closing element of node 'people. Interrupt loop 
                            break;
            }
        }
    }
    

    Output:

    futher
    son
    daughter
    

    UPDATE: Or put node 'person' to simplexml object. Then loop wiil look like:

    while($xmlReader->read()) {
        if ($xmlReader->nodeType == XMLReader::ELEMENT && $xmlReader->name == 'person') {           
            $xml = new SimpleXMLElement($xmlReader->readOuterXML());
            foreach ($xml->name as $name)
                echo $name.'<br>';
       }
    }