XML string to PHP array

One common need when working in PHP is a way to convert an XML document into a serializable array. If you ever tried to serialize() and then unserialize() a SimpleXML or DOMDocument object, you know what I’m talking about.

Assume the following XML snippet:

<tv>
  <show name="Family Guy">
    <dog>Brian</dog>
    <kid>Chris</kid>
    <kid>Meg</kid>
  </show>
</tv>

There’s a quick and dirty way to do convert such a document to an array, using type casting and the JSON functions to ensure there are no exotic values that would cause problems when unserializing:

<?php
  $a = json_decode(json_encode((array) simplexml_load_string($s)),1);
?>

Here is the result for our sample XML, eg if we print_r($a):

Array
(
    [show] => Array
        (
            [@attributes] => Array
                (
                    [name] => Family Guy
                )
            [dog] => Brian
            [kid] => Array
                (
                    [0] => Chris
                    [1] => Meg
                )
        )
)

Pretty nifty, eh? But maybe we want to embed some HTML tags or something crazy along those lines. then we need a CDATA node…

<tv>
  <show name="Family Guy">
    <dog>Brian</dog>
    <kid>Chris</kid>
    <kid>Meg</kid>
    <kid><![CDATA[<em>Stewie</em>]]></kid>
  </show>
</tv>

The snippet of XML above would yield the following:

Array
(
    [show] => Array
        (
            [@attributes] => Array
                (
                    [name] => Family Guy
                )
            [dog] => Brian
            [kid] => Array
                (
                    [0] => Chris
                    [1] => Meg
                    [2] => Array
                        (
                        )
                )
        )
)

That’s not very useful. We got in trouble because the CDATA node, a SimpleXMLElement, is being cast to an array instead of a string. To handle this case while still keeping the nice @attributes notation, we need a slightly more verbose conversion function. Here is my version, hereby released under a do-whatever-but-dont-sue-me license.

<?php
/**
 * convert xml string to php array - useful to get a serializable value
 *
 * @param string $xmlstr
 * @return array
 * @author Adrien aka Gaarf
 */
function xmlstr_to_array($xmlstr) {
  $doc = new DOMDocument();
  $doc->loadXML($xmlstr);
  return domnode_to_array($doc->documentElement);
}
function domnode_to_array($node) {
  $output = array();
  switch ($node->nodeType) {
   case XML_CDATA_SECTION_NODE:
   case XML_TEXT_NODE:
    $output = trim($node->textContent);
   break;
   case XML_ELEMENT_NODE:
    for ($i=0, $m=$node->childNodes->length; $i<$m; $i++) {
     $child = $node->childNodes->item($i);
     $v = domnode_to_array($child);
     if(isset($child->tagName)) {
       $t = $child->tagName;
       if(!isset($output[$t])) {
        $output[$t] = array();
       }
       $output[$t][] = $v;
     }
     elseif($v) {
      $output = (string) $v;
     }
    }
    if(is_array($output)) {
     if($node->attributes->length) {
      $a = array();
      foreach($node->attributes as $attrName => $attrNode) {
       $a[$attrName] = (string) $attrNode->value;
      }
      $output['@attributes'] = $a;
     }
     foreach ($output as $t => $v) {
      if(is_array($v) && count($v)==1 && $t!='@attributes') {
       $output[$t] = $v[0];
      }
     }
    }
   break;
  }
  return $output;
}
?>

and the result, for our Stewie snippet:

Array
(
    [show] => Array
        (
            [@attributes] => Array
                (
                    [name] => Family Guy
                )
            [dog] => Brian
            [kid] => Array
                (
                    [0] => Chris
                    [1] => Meg
                    [2] => <em>Stewie</em>
                )
        )
)

Victory is mine! :D

This entry was posted in geekout, web dev. Bookmark the permalink.

5 Responses to XML string to PHP array

  1. Rasmus says:

    Last time someone asked me how to do this, this was my answer. The benefits here are that it handles xml namespaces nicely:

    function xmlToArray($xml,$ns=null){
    $a = array();
    for($xml->rewind(); $xml->valid(); $xml->next()) {
    $key = $xml->key();
    if(!isset($a[$key])) { $a[$key] = array(); $i=0; }
    else $i = count($a[$key]);
    $simple = true;
    foreach($xml->current()->attributes() as $k=>$v) {
    $a[$key][$i][$k]=(string)$v;
    $simple = false;
    }
    if($ns) foreach($ns as $nid=>$name) {
    foreach($xml->current()->attributes($name) as $k=>$v) {
    $a[$key][$i][$nid.':'.$k]=(string)$v;
    $simple = false;
    }
    }
    if($xml->hasChildren()) {
    if($simple) $a[$key][$i] = xmlToArray($xml->current(), $ns);
    else $a[$key][$i]['content'] = xmlToArray($xml->current(), $ns);
    } else {
    if($simple) $a[$key][$i] = strval($xml->current());
    else $a[$key][$i]['content'] = strval($xml->current());
    }
    $i++;
    }
    return $a;
    }

    $xml = new SimpleXmlIterator(‘./a.xml’, null, true);
    $namespaces = $xml->getNamespaces(true);
    $arr = xmlToArray($xml,$namespaces);

  2. Rasmus says:

    Well, that was a bit ugly. Here is a nicer version: http://codepad.org/5CrZpqeh

  3. Umair Jabbar says:

    hey there,
    i actually need a snippet which can convert a php array to xml string,

  4. ad says:

    Umair, to build XML I suggest you have a look at DOMDocument class. In particular, have a look at http://www.php.net/manual/en/domdocument.savexml.php

  5. ALX-1974 says:

    Thank you so much man!

    You did exactly what i’ve been searching for days.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>