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?

7 Answers 7

78
json_decode(json_encode((array) simplexml_load_string($obj)), 1);
Sign up to request clarification or add additional context in comments.

5 Comments

clever! I would have never thought to do that.
I would have given you 100 upvotes if possible. Its just awesome:)
@AdamLibuša yes but how would you save attributes in a php array anyway?
Seriously, how can this answer by accepted? It does not work for the most simple test case: <?php $xml = '<?xml version="1.0" encoding="utf-8"?><data><empty/></data>'; $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.
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.
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

1 Comment

Not sure how this "doesn't work" for others, but it does the job of iterating through all children and attributes.
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";
}

Comments

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.

3 Comments

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
@GromBeestje: XML is already serialized. There is no problem to store a string in a session :)
Parsing the XML string every time the script loads seems inefficient, therefore I think it makes sense to store a parsed form.
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;
}

Comments

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;
}

Comments

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);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.