rossjha
rossjha

Reputation: 191

PHP XMLReader dealing with missing element

I'm using XMLReader to parse a file which i don't control. The file is missing some elements, example below. Where book 2 is missing an info element, i still want to add this to the array. I realise i could use a combination of XMLReader and SimpleXML... but i'd like to see if this is possible?

<book>
    <title>book 1</title>
    <info>book 1 info</info>
</book>
<book>
    <title>book 2</title>
</book>
<book>
    <title>book 3</title>
    <info>book 3 info</info>
</book>

I have the following XMLReader function:

<?php
function parseXML($args){
    extract($args);
    $xml = new XMLReader();
    $xml->open($file) or die('Cannot open file');

    while($xml->read()){
        switch($xml->nodeType){
            case(XMLREADER::ELEMENT):{
                if(in_array($xml->localName, $nodes, true)){
                    $lName = $xml->localName;
                    $val = $xml->readString();
                    $xml->read();

                    $child[$lName] = trim($val);
                    if($lName == end($nodes)){
                        $arr[] = $child;
                    }
                }
            }
            break;
        }
    }
    $xml->close($file);
    return $arr;
}
$settings = array(
    'file' => 'books.xml',
    'nodes' => array('name', 'info')
);
$books = parseXML($settings);
var_dump($books);
?>

which produces:

Array
(
[0] => Array
    (
        [name] => book 1
        [info] => book 1 info
    )

[1] => Array
    (
        [name] => book 3
        [info] => book 3 info
    )
)

Upvotes: 1

Views: 819

Answers (1)

salathe
salathe

Reputation: 51950

You could rethink your logic a little. The snippet below builds up a $book array for each <book> element, starting each time with default values (an empty string) in case a <name> or <info> does not exist for the book.

As <name> and <info> elements are reached, their contents are added to the current $book array.

When reaching a </book>, the $book array is added to the main array of $books.

Building $books

function parseXML($args){
    extract($args);

    $xml   = new XMLReader();
    $xml->open($file) or die('Cannot open file');

    $books = array();

    while ($xml->read()) {
        // <book> - start a new child array with default values
        if ($xml->nodeType === XMLReader::ELEMENT 
         && $xml->localName === 'book') {
            $book = array_fill_keys($nodes, '');
        // </book> - add the child array to main array
        } elseif ($xml->nodeType === XMLReader::END_ELEMENT 
               && $xml->localName === 'book') {
            $books[] = $book;
        // <info> or <title> - add to child array
        } elseif ($xml->nodeType === XMLReader::ELEMENT 
               && in_array($xml->localName, $nodes, TRUE)) {
            $name = $xml->localName;
            $val  = $xml->readString();
            $book[$name] = trim($val);
        }
    }

    $xml->close();
    return $books;
}

Resulting array

$books = array(
  array('title' => 'book 1', 'info' => 'book 1 info'),
  array('title' => 'book 2', 'info' => ''),
  array('title' => 'book 3', 'info' => 'book 3 info'),
);

Upvotes: 2

Related Questions