I want to convert below XML to PHP array. Any suggestions on how I can do this?
<aaaa Version="1.0">
<bbb>
<cccc>
<dddd Id="id:pass" />
<eeee name="hearaman" age="24" />
</cccc>
</bbb>
</aaaa>
I want to convert below XML to PHP array. Any suggestions on how I can do this?
<aaaa Version="1.0">
<bbb>
<cccc>
<dddd Id="id:pass" />
<eeee name="hearaman" age="24" />
</cccc>
</bbb>
</aaaa>
The method used in the accepted answer drop attributes when encountering child elements with only a text node. For example:
$xml = '<container><element attribute="123">abcd</element></container>';
print_r(json_decode(json_encode(simplexml_load_string($xml, "SimpleXMLElement", LIBXML_NOCDATA)),1));
Array
(
[element] => abcd
)
My solution (and I wish I could give credit here because I'm sure I adapted this from something):
function XMLtoArray($xml) {
$previous_value = libxml_use_internal_errors(true);
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->preserveWhiteSpace = false;
$dom->loadXml($xml);
libxml_use_internal_errors($previous_value);
if (libxml_get_errors()) {
return [];
}
return DOMtoArray($dom);
}
function DOMtoArray($root) {
$result = array();
if ($root->hasAttributes()) {
$attrs = $root->attributes;
foreach ($attrs as $attr) {
$result['@attributes'][$attr->name] = $attr->value;
}
}
if ($root->hasChildNodes()) {
$children = $root->childNodes;
if ($children->length == 1) {
$child = $children->item(0);
if (in_array($child->nodeType,[XML_TEXT_NODE,XML_CDATA_SECTION_NODE])) {
$result['_value'] = $child->nodeValue;
return count($result) == 1
? $result['_value']
: $result;
}
}
$groups = array();
foreach ($children as $child) {
if (!isset($result[$child->nodeName])) {
$result[$child->nodeName] = DOMtoArray($child);
} else {
if (!isset($groups[$child->nodeName])) {
$result[$child->nodeName] = array($result[$child->nodeName]);
$groups[$child->nodeName] = 1;
}
$result[$child->nodeName][] = DOMtoArray($child);
}
}
}
return $result;
}
$xml = '
<aaaa Version="1.0">
<bbb>
<cccc>
<dddd id="123" />
<eeee name="john" age="24" />
<ffff type="employee">Supervisor</ffff>
</cccc>
</bbb>
</aaaa>
';
print_r(XMLtoArray($xml));
Array
(
[aaaa] => Array
(
[@attributes] => Array
(
[Version] => 1.0
)
[bbb] => Array
(
[cccc] => Array
(
[dddd] => Array
(
[@attributes] => Array
(
[id] => 123
)
)
[eeee] => Array
(
[@attributes] => Array
(
[name] => john
[age] => 24
)
)
[ffff] => Array
(
[@attributes] => Array
(
[type] => employee
)
[_value] => Supervisor
)
)
)
)
)
Two lines of code (https://www.php.net/manual/en/book.simplexml.php#113485)
$xml = new SimpleXMLElement("<your><xml><string>ok</string></xml></your>");
$array = (array)$xml;
I know I'm 1 billion years late, but I had the same problem as you and needed a more complex solution, so here is a function (xml_decode()) I made to convert SimpleXMLElements into PHP arrays without losing attributes and with arguments for a more customizable use.
The val() function is meant for you to customize how element values shall be treated - in case you want to transform <elem>true</elem> into true instead of "true" for example.
Disclaimer: I know it is easier to just use the PHP SimpleXML extension, but I needed to transform lots of XML files into JSON files for a big change in a project of mine. Also, the question is about how to transform XML to PHP arrays, not how to use XML in PHP.
<?php
function val($input) {
return strval($input);
}
/**
* Transform an SimpleXMLElement into an associative array.
*
* @param SimpleXMLElement $xml The XML element to be decoded.
*
* @param bool $attributes_key If the element attributes should be grouped into a single element.
*
* Example: <elem foo="true" bar="false" />
*
* If true, xml_decode() will output
* array("attributes" => array("foo" => "true", "bar" => "false"))
*
* If false, xml_decode() will output
* array("foo" => "true", "bar" => "false")
*
* @param bool $reduce If unecessary keys created due to XML structure should be eliminated.
*
* Example: <fruits><fruit>apple</fruit><fruit>banana</fruit></fruits>
*
* If true, xml_decode() will output the element as
* array("fruits" => array(0 => "apple", 1 => "banana"))
*
* If false, xml_decode() will output the element as
* array("fruits" => array("fruit" => array(0 => "apple", 1 => "banana")))
*
* @param array $always_array List of which childs should be treated aways as an array.
*
* Example: <fruits><fruit>apple</fruit></fruits>
*
* If array("fruit") is passed as $aways_array, xml_decode() will output the element as
* array("fruits" => array("fruit" => array(0 => "apple")))
*
* If not, xml_decode() will output the element as
* array("fruits" => array("fruit" => "apple"))
*
* @param array $value_keys List of custom element's value names. This argument is only
* used when values need to passed as elements because of attributes or other reasons.
*
* The default value key name is "value".
*
* Example: <fruits><fruit id="123">apple</fruit></fruits>
*
* If array("fruit" => "name) is passed as $value_keys, xml_decode() will output the element as
* array("fruits" => array("fruit" => array("attributes" => array("id" => "123"), "name" => "apple")))
*
* If not, xml_decode() will output the element as
* array("fruits" => array("fruit" => array("attributes" => array("id" => "123"), "value" => "apple")))
*/
function xml_decode(SimpleXMLElement $xml, bool $attributes_key = true, bool $reduce = true,
array $always_array = array(), array $value_keys = array()): string|array {
// Inicialize the array.
$arr = array();
// XML tag name.
$xml_name = $xml->getName();
// Turn attributes into elements.
foreach ($xml->attributes() as $key => $value) {
// Use a key for attributes if $attributes_key argument is true.
if ($attributes_key) {
$arr['attributes'][val($key)] = val($value);
} else {
$arr[val($key)] = val($value);
}
}
// Count children.
$children_count = $xml->children()->count();
// No children? Value will be text.
if ($children_count == 0) {
// If attributes were found and turned into elements
// the value shall be an element.
if (count($arr) > 0) {
// If attributes were found previosly.
$key = $value_keys[$xml_name] ?? $value_keys['*'] ?? "value";
$arr[$key] = val($xml);
// Else, no need for an array.
} else {
$arr = val($xml);
}
// Children? Loop continues.
} else {
// Defines if there are unecessary array keys - due to the XML structure - to be cut.
// Example: <fruits><fruit /><fruit /><fruits />
// could be turned into arr['fruits'][0] and arr['fruits'][1] instead of
// arr['fruits']['fruit'][0] and arr['fruits']['fruit'][1] for a
// cleaner organization.
$children_names = array();
foreach ($xml->children() as $child) {
$child_name = $child->getName();
in_array($child_name, $children_names) or $children_names[] = $child_name;
}
$reducible = empty($arr) && count($children_names) === 1;
foreach ($xml->children() as $child) {
// Child's name shall be the element key.
$name = $child->getName();
// Children with the same name will be turned into a list.
// Example: $arr['repeating-child'][...] = $value;
if ($xml->$name->count() > 1 || in_array($name, $always_array)) {
// Reduction, if possible and requested by the $reduce argument.
if ($reduce && $reducible) {
$arr[] = xml_decode($child, $attributes_key, $reduce, $always_array, $value_keys);
} else {
$arr[$name][] = xml_decode($child, $attributes_key, $reduce, $always_array, $value_keys);
}
// Normal children will be normally decoded.
// Example: $arr['no-repeating-child] = $value;
} else {
$arr[$name] = xml_decode($child, $attributes_key, $reduce, $always_array, $value_keys);
}
}
}
return $arr;
}
Resuming all the documentation and comments, the function transforms attributes and elements values into simple array elements and uses a loop with itself to process elements which contain children.
The arguments allow you to:
fruits->fruit to $arr['fruits']['fruit'][n]);Usage example with your XML elements (I think you already solved it after 11 years, but I'm answering it, so...):
test.xml
<test>
<aaaa Version="1.0">
<bbb>
<cccc>
<dddd Id="id:pass" />
<eeee name="hearaman" age="24" />
</cccc>
</bbb>
</aaaa>
</test>
PHP
$xml = simplexml_load_file("test.xml");
$decode = xml_decode($xml);
echo "<pre>" . print_r($decode,true) . "</pre>";
Output
Array
(
[aaaa] => Array
(
[attributes] => Array
(
[Version] => 1.0
)
[bbb] => Array
(
[cccc] => Array
(
[dddd] => Array
(
[attributes] => Array
(
[Id] => id:pass
)
[value] =>
)
[eeee] => Array
(
[attributes] => Array
(
[name] => hearaman
[age] => 24
)
[value] =>
)
)
)
)
)