I am trying to convert an XML string into multi-dimensioned PHP array. The difficulties are that XML comes with attributes and has nested values. My code works at parent level data but I am not sure how to deal with sub-levels recursively.
A sample XML is as follows:
<root>
<item id="1" name="ItemOne">Item Value 1</item>
<item id="2" name="ItemTwo">
<subb>Sub Value 1</subb>
<subb>Sub Value 2</subb>
</item>
<item id="3" name="ItemThree">Value 3</item>
<something>something value</something>
</root>
This is my current function to achieve this:
$xmlString = '<root><item id="1" name="ItemOne">Item Value 1</item><item id="2" name="ItemTwo"><subb>Sub Value 1</subb><subb>Sub Value 2</subb></item><item id="3" name="ItemThree">Value 3</item><something>something value</something></root>';
function xmlToArray($xmlObject) {
$array = [];
foreach ($xmlObject as $item) {
// Convert attributes to an array
$attributes = (array)$item->attributes();
// Add the text value to the attributes array if it exists
$attributes['@value'] = trim((string)$item);
$key = (string)$item->getName();
if (isset($make_sub_array) && in_array($key, $make_sub_array)) {
$array[$key][] = $attributes;
}
elseif (isset($array[$key])) {
$make_sub_array[] = $key;
$tmp = $array[$key];
unset($array[$key]);
$array[$key][] = $tmp; //existing data
$array[$key][] = $attributes; //this data
}
else $array[$key] = $attributes;
}
return $array;
}
// Load the XML string into a SimpleXMLElement object
$xmlObject = simplexml_load_string($xmlString);
$array = xmlToArray($xmlObject);
exit('<pre>'.print_r($array,1).'</pre>');
The resulting array structure is below, and I require your help about how I can process the array under second item. I would like it to be processed the same way as the parent one: if the item name is repeated then it will be included as [] so I get number as its parent, otherwise [itemname]. Thank you
Array
(
[item] => Array
(
[0] => Array
(
[@attributes] => Array
(
[id] => 1
[name] => ItemOne
)
[@value] => Item Value 1
)
[1] => Array
(
[@attributes] => Array
(
[id] => 2
[name] => ItemTwo
)
[@value] =>
)
[2] => Array
(
[@attributes] => Array
(
[id] => 3
[name] => ItemThree
)
[@value] => Value 3
)
)
[something] => Array
(
[@value] => something value
)
)
If you replace your:
$attributes['@value'] = trim((string)$item);
by:
$attributes['@value'] = count($item->children()) ? xmlToArray($item) : trim((string)$item);
you'll get what you want:
<pre>Array
(
[item] => Array
(
[0] => Array
(
[@attributes] => Array
(
[id] => 1
[name] => ItemOne
)
[@value] => Item Value 1
)
[1] => Array
(
[@attributes] => Array
(
[id] => 2
[name] => ItemTwo
)
[@value] => Array
(
[subb] => Array
(
[0] => Array
(
[@value] => Sub Value 1
)
[1] => Array
(
[@value] => Sub Value 2
)
)
)
)
[2] => Array
(
[@attributes] => Array
(
[id] => 3
[name] => ItemThree
)
[@value] => Value 3
)
)
[something] => Array
(
[@value] => something value
)
)
</pre>
Note that hasChildren()
will consistently return false
, thus the test with count($item->children())
.