23

Is there any more elegant way to escape SimpleXML attributes to an array?

$result = $xml->xpath( $xpath );
$element = $result[ 0 ];
$attributes = (array) $element->attributes();
$attributes = $attributes[ '@attributes' ];

I don't really want to have to loop through it just to extract the key/value pair. All I need is to get it into an array and then pass it on. I would have thought attributes() would have done it by default, or at least given the option. But I couldn't even find the above solution anywhere, I had to figure that out on my own. Am I over complicating this or something?

Edit:

I'm still using the above script until I know for sure whether accessing the @attributes array is safe or not.

5 Answers 5

63

a more elegant way; it gives you the same results without using $attributes[ '@attributes' ] :

$attributes = current($element->attributes());
Sign up to request clarification or add additional context in comments.

1 Comment

Nice, thank You! I usually add current(...) ?: [] because in case the XML element has no attributes, current(...) returns false where empty array would be (imho) more appropriate.
12

Don't directly read the '@attributes' property, that's for internal use. Anyway, attributes() can already be used as an array without needing to "convert" to a real array.

For example:

<?php
$xml = '<xml><test><a a="b" r="x" q="v" /></test><b/></xml>';
$x = new SimpleXMLElement($xml);

$attr = $x->test[0]->a[0]->attributes();
echo $attr['a']; // "b"

If you want it to be a "true" array, you're gonna have to loop:

$attrArray = array();
$attr = $x->test[0]->a[0]->attributes();

foreach($attr as $key=>$val){
    $attrArray[(string)$key] = (string)$val;
}

8 Comments

Yes, but the problem with this is that it still thinks of itself as a SimpleXML element and therefore you would have to typecast $attr[ 'a' ] to a string for it to work properly. I'm passing this array to another class that doesn't know what type its supposed to be, only that it needs to be an array.
Ah, you got it in the edit... Is the loop "better" than what I'm currently doing? I would think that doing so without a loop would be quicker.
@showerhead: I don't know if it's better, but I've always learned not to directly read the '@attributes' property.
@showerhead: Maybe I'm wrong, I'm not sure where I got that from. If it works for you, I say use it. The docs don't mention '@attributes' at all, neither use it nor not use it.
I'll keep an eye out for it. Assuming no other answers appear I'll accept this one later tonight. Though I think I'll do some performance tests on the two solutions to see which one works better.
|
4

You could convert the whole xml document into an array:

$array = json_decode(json_encode((array) simplexml_load_string("<response>{$xml}</response>")), true);

For more information see: https://github.com/gaarf/XML-string-to-PHP-array

Comments

1

For me below method worked

function xmlToArray(SimpleXMLElement $xml)
{
    $parser = function (SimpleXMLElement $xml, array $collection = []) use (&$parser) {
        $nodes = $xml->children();
        $attributes = $xml->attributes();

        if (0 !== count($attributes)) {
            foreach ($attributes as $attrName => $attrValue) {
                $collection['@attributes'][$attrName] = strval($attrValue);
            }
        }

        if (0 === $nodes->count()) {
            if($xml->attributes())
            {
                $collection['value'] = strval($xml);
            }
            else
            {
                $collection = strval($xml);
            }
            return $collection;
        }

        foreach ($nodes as $nodeName => $nodeValue) {
            if (count($nodeValue->xpath('../' . $nodeName)) < 2) {
                $collection[$nodeName] = $parser($nodeValue);
                continue;
            }

            $collection[$nodeName][] = $parser($nodeValue);
        }

        return $collection;
    };

    return [
        $xml->getName() => $parser($xml)
    ];
}

This also provides me all the attributes as well, which I didn't get from any other method.

Comments

0

I think you will have to loop through. You can get it into array once you read xml.

<?php
function objectsIntoArray($arrObjData, $arrSkipIndices = array())
{
$arrData = array();

// if input is object, convert into array
if (is_object($arrObjData)) {
    $arrObjData = get_object_vars($arrObjData);
}

if (is_array($arrObjData)) {
    foreach ($arrObjData as $index => $value) {
        if (is_object($value) || is_array($value)) {
            $value = objectsIntoArray($value, $arrSkipIndices); // recursive call
        }
        if (in_array($index, $arrSkipIndices)) {
            continue;
        }
        $arrData[$index] = $value;
    }
}
return $arrData;
}

$xmlStr = file_get_contents($xml_file);
$xmlObj = simplexml_load_string($xmlStr);
$arrXml = objectsIntoArray($xmlObj);

foreach($arrXml as $attr)
  foreach($attr as $key->$val){
 if($key == '@attributes') ....
}

2 Comments

What is objectsIntoArray? Also, you shouldn't read the '@attributes' directly, that's what ->attributes() is for.
Sorry, I pasted from my code missing top part just edited it.

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.