> I've tried simplexml and a few third-party scripts, one that loads the
> xml into an associative array rather than an object, and nothing lets me
> get at 'red' or 'blue'.
I needed something similar for my module. First of all, I wanted it to be compatible with PHP4. This means I needed to use xml_parser functions. Second, I didn't want the user to have to install another module or library.
I ended up using two functions (one is recursive):
/**
* Takes an XML string, and returns a multidimensional array. Each "dimension"
* may be a text string, a key-value array of tag attributes, and/or another
* multidimensional array containing child nodes.
*
* @param $string
* String containing XML data to parse
* @return
* Multidimensional array with attributes, values, and children
*/
function _discogs_xml_to_array(&$string) {
$parser = xml_parser_create();
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
xml_parse_into_struct($parser, $string, $vals, $index);
xml_parser_free($parser);
$multi_array = array();
foreach ($vals as $val) {
$tag = $val['tag'];
if ($val['type'] == 'open') {
if (isset($multi_array[$tag])) {
if (isset($multi_array[$tag][0])) {
$multi_array[$tag][]=array();
}
else {
$multi_array[$tag]=array($multi_array[$tag], array());
}
$cv = &$multi_array[$tag][count($multi_array[$tag])-1];
}
else {
$cv = &$multi_array[$tag];
}
if (isset($val['attributes'])) {
foreach ($val['attributes'] as $k => $v) {
$cv['_attributes'][$k] = $v;
}
}
$cv['_nodes'] = array();
$cv['_nodes']['_recursion'] = &$multi_array;
$multi_array = &$cv['_nodes'];
}
elseif ($val['type'] == 'complete') {
if (isset($multi_array[$tag])) {
if (isset($multi_array[$tag][0])) {
$multi_array[$tag][] = array();
}
else {
$multi_array[$tag] = array($multi_array[$tag], array());
}
$cv = &$multi_array[$tag][count($multi_array[$tag])-1];
}
else {
$cv = &$multi_array[$tag];
}
if (isset($val['attributes'])) {
foreach ($val['attributes'] as $k => $v) {
$cv['_attributes'][$k] = $v;
}
}
if (isset($val['value'])) {
$cv['value'] = trim($val['value']);
}
}
elseif ($val['type'] == 'close') {
$multi_array = &$multi_array['_recursion'];
}
}
// Clean up the recursion and merge attributes with child nodes
$multi_array = _discogs_xml_clean($multi_array);
return $multi_array;
}
/**
* Helper function for _discogs_xml_to_array.
*
* Cleans up the array structure, by getting rid of the recursive data, and
* merging the values, attributes and child nodes.
*
* @param array $old_array
* Old array to clean up
* @return array $new_array
* Cleaned up array
*/
function _discogs_xml_clean($old_array) {
// Double check that it's an array, for recursion
if (!is_array($old_array)) {
return $old_array;
}
foreach ($old_array as $k => $v) {
if ($k === '_recursion') {
unset($old_array[$k]);
}
elseif (is_array($old_array[$k]['_nodes']) && is_array($old_array[$k]['_attributes'])) {
$old_nodes = _discogs_xml_clean($old_array[$k]['_nodes']);
$new_merged = array_merge($old_array[$k]['_attributes'], $old_nodes);
$new_array[$k] = $new_merged;
}
elseif (isset($old_array[$k]['value']) && is_array($old_array[$k]['_attributes'])) {
$value['value'] = trim($old_array[$k]['value']);
$new_merged = array_merge($old_array[$k]['_attributes'], $value);
$new_array[$k] = $new_merged;
}
elseif (is_array($old_array[$k]['_nodes'])) {
$new_array[$k] = _discogs_xml_clean($old_array[$k]['_nodes']);
}
elseif (isset($old_array[$k]['_attributes'])) {
$new_array[$k] = $old_array[$k]['_attributes'];
}
elseif (isset($old_array[$k]['value'])) {
$new_array[$k] = $old_array[$k]['value'];
}
else {
$old_nodes = _discogs_xml_clean($old_array[$k]);
$new_array[$k] = $old_nodes;
}
}
return $new_array;
}
So, if you passed your XML file to the first function, this would be the resulting multidimensional array:
Array
(
[this] => Array
(
[that] => Array
(
[0] => Array
(
[parm1] => a
[parm2] => b
[parm3] => c
[value] => red
)
[1] => Array
(
[parm1] => e
[parm2] => f
[parm3] => d
[value] => blue
)
)
)
)
It's complicated, but it works.
-Karl.