Convert PHP array from XML containing duplicate elements

So far, I have used the snippet below to convert an XML tree to an array:

$a = json_decode(json_encode((array) simplexml_load_string($xml)),1); 

.. however, I am now working with XML, which has duplicate key values, so the array breaks when it passes through XML. For example:

 <users> <user>x</user> <user>y</user> <user>z</user> </users> 

Is there a better way to do this that allows duplicate keys, or perhaps a way to add an extra value to each key when it splashes out an array, for example:

 $array = array( users => array( user_1 => x, user_2 => y, user_3 => z ) ) 

I'm at a standstill, so any help would be greatly appreciated.

+7
arrays xml php
source share
3 answers

Here is a complete universal recursive solution.

This class will parse any XML under any structure, with or without tags, from the simplest to the most complex.

It saves all the correct values ​​and converts them (bool, txt or int), generates adequate array keys for all groups of elements, including tags, saves duplicate elements, etc. etc.

Please forgive the stats, this is part of a large set of XML tools that I used before rewriting them all for HHVM or pthreads, I don’t have time to properly build this one, but it will work like a charm for simple PHP.

For tags, the declared value is "@attr" in this case, but may be any of your needs.

 $xml = "<body> <users id='group 1'> <user>x</user> <user>y</user> <user>z</user> </users> <users id='group 2'> <user>x</user> <user>y</user> <user>z</user> </users> </body>"; $result = xml_utils::xml_to_array($xml); 

result:

 Array ( [users] => Array ( [0] => Array ( [user] => Array ( [0] => x [1] => y [2] => z ) [@attr] => Array ( [id] => group 1 ) ) [1] => Array ( [user] => Array ( [0] => x [1] => y [2] => z ) [@attr] => Array ( [id] => group 2 ) ) ) ) 

Grade:

 class xml_utils { /*object to array mapper */ public static function objectToArray($object) { if (!is_object($object) && !is_array($object)) { return $object; } if (is_object($object)) { $object = get_object_vars($object); } return array_map('objectToArray', $object); } /* xml DOM loader*/ public static function xml_to_array($xmlstr) { $doc = new DOMDocument(); $doc->loadXML($xmlstr); return xml_utils::dom_to_array($doc->documentElement); } /* recursive XMl to array parser */ public static function dom_to_array($node) { $output = array(); switch ($node->nodeType) { case XML_CDATA_SECTION_NODE: case XML_TEXT_NODE: $output = trim($node->textContent); break; case XML_ELEMENT_NODE: for ($i = 0, $m = $node->childNodes->length; $i < $m; $i++) { $child = $node->childNodes->item($i); $v = xml_utils::dom_to_array($child); if (isset($child->tagName)) { $t = xml_utils::ConvertTypes($child->tagName); if (!isset($output[$t])) { $output[$t] = array(); } $output[$t][] = $v; } elseif ($v) { $output = (string) $v; } } if (is_array($output)) { if ($node->attributes->length) { $a = array(); foreach ($node->attributes as $attrName => $attrNode) { $a[$attrName] = xml_utils::ConvertTypes($attrNode->value); } $output['@attr'] = $a; } foreach ($output as $t => $v) { if (is_array($v) && count($v) == 1 && $t != '@attr') { $output[$t] = $v[0]; } } } break; } return $output; } /* elements converter */ public static function ConvertTypes($org) { if (is_numeric($org)) { $val = floatval($org); } else { if ($org === 'true') { $val = true; } else if ($org === 'false') { $val = false; } else { if ($org === '') { $val = null; } else { $val = $org; } } } return $val; } } 
+1
source share

You can scroll each key in your result, and if this value is an array (as for user , which has 3 elements in your example), you can add each individual value in this array to the parent array and disable the value:

 foreach($a as $user_key => $user_values) { if(!is_array($user_values)) continue; //not an array nothing to do unset($a[$user_key]); //it an array so remove it from parent array $i = 1; //counter for new key //add each value to the parent array with numbered keys foreach($user_values as $user_value) { $new_key = $user_key . '_' . $i++; //create new key ie 'user_1' $a[$new_key] = $user_value; //add it to the parent array } } var_dump($a); 
0
source share

First of all, this line of code contains redundant listing in array :

 $a = json_decode(json_encode((array) simplexml_load_string($xml)),1); ^^^^^^^ 

When you JSON code SimpleXMLElement (which is returned by simplexml_load_string when the parameter can be parsed as XML), it already behaves as if it were an array. So it is better to remove it:

 $sxml = simplexml_load_string($xml); $array = json_decode(json_encode($sxml), 1); 

Even the result is the same, now it allows you to create a subtype of SimpleXMLElement that implements the JsonSerialize interface to change the creation of the array according to your needs.

The general method (as well as the default behavior) is described in the blog of my series , on Stackoverflow I left some more several examples:

  • PHP converts XML to JSON group when there is one child (June 2013)
  • Allow namespaces with SimpleXML regardless of structure or namespace (October 2014)
  • Converting XML to JSON in PHP SimpleXML (December 2014)

Your case, I think, looks like what was asked in the first of these three links.

0
source share

All Articles