> 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.