28

I need to recursively cast a PHP SimpleXMLObject to an array. The problem is that each sub element is also a PHP SimpleXMLElement.

Is this possible?

Seb
  • 24,920
  • 5
  • 67
  • 85
Glen Solsberry
  • 11,960
  • 15
  • 69
  • 94

7 Answers7

77
json_decode(json_encode((array) simplexml_load_string($obj)), 1);
  • 1
    clever! I would have never thought to do that. – General Redneck Feb 27 '13 at 21:28
  • 4
    I would have given you 100 upvotes if possible. Its just awesome:) – Arvind Bhardwaj Aug 14 '13 at 10:03
  • 1
    @AdamLibuša yes but how would you save attributes in a php array anyway? – Jeff Puckett Jan 08 '18 at 22:35
  • Seriously, how can this answer by accepted? It does not work for the most simple test case: ```'; $array = json_decode(json_encode((array) simplexml_load_string($xml)), 1); var_dump($array); ?>``` `empty` will be converted as an empty array instead of `null`. – Eric MORAND Feb 27 '19 at 14:12
  • Has another big drawback and converts everything to strings. But when you have booleans or integers they get all casted to strings which is not optimal. – Robert Jun 22 '19 at 11:29
6

Didn't test this one, but this seems to get it done:

function convertXmlObjToArr($obj, &$arr) 
{ 
    $children = $obj->children(); 
    foreach ($children as $elementName => $node) 
    { 
        $nextIdx = count($arr); 
        $arr[$nextIdx] = array(); 
        $arr[$nextIdx]['@name'] = strtolower((string)$elementName); 
        $arr[$nextIdx]['@attributes'] = array(); 
        $attributes = $node->attributes(); 
        foreach ($attributes as $attributeName => $attributeValue) 
        { 
            $attribName = strtolower(trim((string)$attributeName)); 
            $attribVal = trim((string)$attributeValue); 
            $arr[$nextIdx]['@attributes'][$attribName] = $attribVal; 
        } 
        $text = (string)$node; 
        $text = trim($text); 
        if (strlen($text) > 0) 
        { 
            $arr[$nextIdx]['@text'] = $text; 
        } 
        $arr[$nextIdx]['@children'] = array(); 
        convertXmlObjToArr($node, $arr[$nextIdx]['@children']); 
    } 
    return; 
} 

Taken from http://www.codingforums.com/showthread.php?t=87283

Seb
  • 24,920
  • 5
  • 67
  • 85
0

Depending on some troubles with CDATA, arrays etc. (see: SimpleXMLElement to PHP Array)

I think, this would be the best solution:

public function simpleXml2ArrayWithCDATASupport($xml)
{
    $array = (array)$xml;

    if (count($array) === 0) {
        return (string)$xml;
    }

    foreach ($array as $key => $value) {
        if (is_object($value) && strpos(get_class($value), 'SimpleXML') > -1) {
            $array[$key] = $this->simpleXml2ArrayWithCDATASupport($value);
        } else if (is_array($value)) {
            $array[$key] = $this->simpleXml2ArrayWithCDATASupport($value);
        } else {
            continue;
        }
    }

    return $array;
}
Gollm
  • 120
  • 5
0

Here my iterative (even if I don't think you will get a stack explosion by parsing data with a recursive one) implementation of a recursive cast to array. This is a more direct manner of doing it than passing through json_**decode functions:

function xml2Array(SimpleXMLElement $el): stdClass {
    $ret = $el;
    $stack = [&$ret];
    while (count($stack) > 0) {
        $cur = &$stack[count($stack) - 1];
        array_splice($stack, -1);
        $cur = (object) (array) $cur;
        foreach ($cur as $key => $child) {
            $childRef = &$cur->{$key};
            if ($child instanceof SimpleXMLElement)
                $stack[count($stack) - 1] = &$childRef;
            elseif(is_array($child))
                foreach ($childRef as $ckey => $cell) {
                    if ($cell instanceof SimpleXMLElement)
                        $stack[count($stack) - 1] = &$childRef[$ckey];
                }
        }
    }
    return $ret;
}
eytienne
  • 103
  • 3
  • 11
0

For those of you who have concerns about the CDATA case,

combining @ajayi-oluwaseun-emmanuel's answer with this answer worked for me:

$xml = simplexml_load_string($xml_str, 'SimpleXMLElement', LIBXML_NOCDATA);
$json = json_encode($xml);
$arr = json_decode($json,TRUE);
MGoksu
  • 510
  • 6
  • 13
0

It is possible. This is a recursive function which prints out the tags of parent elements and the tags + contents of elements that have no more children. You can alter it to build an array:

foreach( $simpleXmlObject as $element )
{
    recurse( $element );
}

function recurse( $parent )
{
    echo '<' . $parent->getName() . '>' . "\n";    

    foreach( $parent->children() as $child )
    {
        if( count( $child->children() ) > 0 )
        {
            recurse( $child );
        }
        else
        {
           echo'<' . $child->getName() . '>';
           echo  iconv( 'UTF-8', 'ISO-8859-1', $child );
           echo '</' . $child->getName() . '>' . "\n";
        }
    }

   echo'</' . $parent->getName() . '>' . "\n";
}
Petrunov
  • 754
  • 1
  • 8
  • 18
0

I don't see the point since SimpleXMLObject can be threated just like arrays anyway...

But if you really need that, just check chassagnette's answer of in this thread or this post in a forum.

Bite code
  • 578,959
  • 113
  • 301
  • 329
  • 2
    Except for the situation when you want to store it in a session, getting 'Serialization of 'SimpleXMLElement' is not allowed' when I try that. Therefore casting to array is usefull – André van Schoubroeck Feb 07 '13 at 22:30
  • 1
    @GromBeestje: XML is already serialized. There is no problem to store a string in a session :) – hakre May 02 '15 at 10:14
  • Parsing the XML string every time the script loads seems inefficient, therefore I think it makes sense to store a parsed form. – André van Schoubroeck May 30 '15 at 09:06