Home

Algorithmic Advent: 06 – Transform DOMNode to array

Published on 6 Dec 2010. Tagged with php, algorithmicadvent.

/**
 * Transforms the contents of a DOMNode to an associative array
 * 
 * XML node names become array keys, attributes will be discarded. If $node
 * doesn't contain child nodes, a string will be returned.
 *
 * For more information about DOMNode and related classes see the documentation
 * of DOMDocument <http://php.net/manual/en/class.domdocument.php>.
 *
 * @param DOMNode $node DOMDocument node
 * @return string|array Associative array or string with node content
 */
function domNodeToArray(DOMNode $node)
{
    $ret = '';

    if ($node->hasChildNodes()) {
        if ($node->firstChild === $node->lastChild
            && $node->firstChild->nodeType === XML_TEXT_NODE
        ) {
            // Node contains nothing but a text node, return its value
            $ret = trim($node->nodeValue);
        } else {
            // Otherwise, do recursion
            $ret = array();
            foreach ($node->childNodes as $child) {
                if ($child->nodeType !== XML_TEXT_NODE) {
                    // If there's more than one node with this node name on the
                    // current level, create an array
                    if (isset($ret[$child->nodeName])) {
                        if (!is_array($ret[$child->nodeName])
                            || !isset($ret[$child->nodeName][0])
                        ) {
                            $tmp = $ret[$child->nodeName];
                            $ret[$child->nodeName] = array();
                            $ret[$child->nodeName][] = $tmp;
                        }
                        
                        $ret[$child->nodeName][] = domNodeToArray($child);
                    } else {
                        $ret[$child->nodeName] = domNodeToArray($child);
                    }
                }
            }
        }
    }

    return $ret;
}

Usage example:

$xmlCode = <<<EOT
<?xml version="1.0" encoding="utf-8"?>
<characters>
  <character>
    <name>Steve</name>
    <role>King</role>
    <stats>
      <age>52</age>
      <weight>85</weight>
    </stats>
  </character>
  <character>
    <name>Johnny</name>
    <role>Knight</role>
    <role>Jester</role>
  </character>
</characters>
EOT;

$dom = new DOMDocument();
$dom->loadXML($xmlCode);

$array = array();

$xpath = new DOMXPath($dom);
foreach ($xpath->query('/characters', $dom) as $node) {
    $array = domNodeToArray($node);
}

echo '<pre>';
print_r($array);

This will return the following array structure:

Array
(
    [character] => Array
        (
            [0] => Array
                (
                    [name] => Steve
                    [role] => King
                    [stats] => Array
                        (
                            [age] => 52
                            [weight] => 85
                        )

                )

            [1] => Array
                (
                    [name] => Johnny
                    [role] => Array
                        (
                            [0] => Knight
                            [1] => Jester
                        )

                )

        )

)