Up until now, I've been using the snippet below to convert an XML tree to an array:
到目前为止,我一直在使用下面的代码片段将XML树转换为数组:
$a = json_decode(json_encode((array) simplexml_load_string($xml)),1);
..however, I'm now working with an XML that has duplicate key values, so the array is breaking when it loops through the XML. For example:
. .但是,我现在使用的是一个具有重复键值的XML,所以当数组在XML中循环时,数组就会崩溃。例如:
<users>
<user>x</user>
<user>y</user>
<user>z</user>
</users>
Is there a better method to do this that allows for duplicate Keys, or perhaps a way to add an incremented value to each key when it spits out the array, like this:
是否有更好的方法允许重复键,或者是否有一种方法可以在每个键输出数组时向每个键添加一个递增的值,比如:
$array = array(
users => array(
user_1 => x,
user_2 => y,
user_3 => z
)
)
I'm stumped, so any help would be very appreciated.
我被难住了,所以任何帮助都将非常感谢。
3 个解决方案
#1
1
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 ones.
这个类将解析任何结构下的XML,不管有没有标记,从最简单的到最复杂的。
It retains all proper values and convert them (bool, txt or int), generates adequate array keys for all elements groups including tags, keep duplicates elements etc etc...
它保留所有正确的值并转换它们(bool, txt或int),为所有元素组(包括标签)生成足够的数组键,保持重复的元素等等……
Please forgive the statics, it s part of a large XML tools set I used, before rewriting them all for HHVM or pthreads, I havent got time to properly construct this one, but it will work like a charm for straightforward PHP.
请原谅这些静态问题,它是我使用的一个大型XML工具集的一部分,在将它们全部重写为HHVM或pthreads之前,我没有时间正确地构造这个工具,但是对于简单的PHP来说,它将会非常有用。
For tags, the declared value is '@attr' in this case but can be whatever your needs are.
对于标记,在本例中声明的值是“@attr”,但可以是您需要的任何值。
$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 ) ) ) )
Class:
类:
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;
}
}
#2
0
You can loop through each key in your result and if the value is an array (as it is for user
that has 3 elements in your example) then you can add each individual value in that array to the parent array and unset the value:
您可以对结果中的每个键进行循环,如果该值是一个数组(例如在您的示例中有3个元素的用户),那么您可以将该数组中的每个单独的值添加到父数组,并取消设置值:
foreach($a as $user_key => $user_values) {
if(!is_array($user_values))
continue; //not an array nothing to do
unset($a[$user_key]); //it's 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 i.e 'user_1'
$a[$new_key] = $user_value; //add it to the parent array
}
}
var_dump($a);
#3
0
First of all this line of code contains a superfluous cast to array
:
首先,这一行代码包含对数组的多余转换:
$a = json_decode(json_encode((array) simplexml_load_string($xml)),1);
^^^^^^^
When you JSON-encode a SimpleXMLElement (which is returned by simplexml_load_string
when the parameter could be parsed as XML) this already behaves as-if there would have been an array cast. So it's better to remove it:
当您对SimpleXMLElement进行json编码(simplexml_load_string在将参数解析为XML时返回该元素)时,它的行为就已经是这样了——如果存在数组转换的话。所以最好把它去掉:
$sxml = simplexml_load_string($xml);
$array = json_decode(json_encode($sxml), 1);
Even the result is still the same, this now allows you to create a subtype of SimpleXMLElement implementing the JsonSerialize interface changing the array creation to your needs.
即使结果仍然相同,现在也允许您创建SimpleXMLElement的子类型,该子类型实现JsonSerialize接口,并根据需要更改数组的创建。
The overall method (as well as the default behaviour) is outlined in a blog-series of mine, on * I have left some more examples already as well:
总体方法(以及默认行为)在我的博客系列中有概述,在*上,我已经留下了更多的例子:
- PHP convert XML to JSON group when there is one child (Jun 2013)
- 当有一个子节点时,PHP将XML转换为JSON组(2013年6月)
- Resolve namespaces with SimpleXML regardless of structure or namespace (Oct 2014)
- 使用SimpleXML解决名称空间,不管结构或名称空间(2014年10月)
- XML to JSON conversion in PHP SimpleXML (Dec 2014)
- XML到JSON转换的PHP SimpleXML(2014年12月)
Your case I think is similar to what has been asked in the first of those three links.
我认为你的案例与这三个环节中的第一个相似。
#1
1
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 ones.
这个类将解析任何结构下的XML,不管有没有标记,从最简单的到最复杂的。
It retains all proper values and convert them (bool, txt or int), generates adequate array keys for all elements groups including tags, keep duplicates elements etc etc...
它保留所有正确的值并转换它们(bool, txt或int),为所有元素组(包括标签)生成足够的数组键,保持重复的元素等等……
Please forgive the statics, it s part of a large XML tools set I used, before rewriting them all for HHVM or pthreads, I havent got time to properly construct this one, but it will work like a charm for straightforward PHP.
请原谅这些静态问题,它是我使用的一个大型XML工具集的一部分,在将它们全部重写为HHVM或pthreads之前,我没有时间正确地构造这个工具,但是对于简单的PHP来说,它将会非常有用。
For tags, the declared value is '@attr' in this case but can be whatever your needs are.
对于标记,在本例中声明的值是“@attr”,但可以是您需要的任何值。
$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 ) ) ) )
Class:
类:
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;
}
}
#2
0
You can loop through each key in your result and if the value is an array (as it is for user
that has 3 elements in your example) then you can add each individual value in that array to the parent array and unset the value:
您可以对结果中的每个键进行循环,如果该值是一个数组(例如在您的示例中有3个元素的用户),那么您可以将该数组中的每个单独的值添加到父数组,并取消设置值:
foreach($a as $user_key => $user_values) {
if(!is_array($user_values))
continue; //not an array nothing to do
unset($a[$user_key]); //it's 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 i.e 'user_1'
$a[$new_key] = $user_value; //add it to the parent array
}
}
var_dump($a);
#3
0
First of all this line of code contains a superfluous cast to array
:
首先,这一行代码包含对数组的多余转换:
$a = json_decode(json_encode((array) simplexml_load_string($xml)),1);
^^^^^^^
When you JSON-encode a SimpleXMLElement (which is returned by simplexml_load_string
when the parameter could be parsed as XML) this already behaves as-if there would have been an array cast. So it's better to remove it:
当您对SimpleXMLElement进行json编码(simplexml_load_string在将参数解析为XML时返回该元素)时,它的行为就已经是这样了——如果存在数组转换的话。所以最好把它去掉:
$sxml = simplexml_load_string($xml);
$array = json_decode(json_encode($sxml), 1);
Even the result is still the same, this now allows you to create a subtype of SimpleXMLElement implementing the JsonSerialize interface changing the array creation to your needs.
即使结果仍然相同,现在也允许您创建SimpleXMLElement的子类型,该子类型实现JsonSerialize接口,并根据需要更改数组的创建。
The overall method (as well as the default behaviour) is outlined in a blog-series of mine, on * I have left some more examples already as well:
总体方法(以及默认行为)在我的博客系列中有概述,在*上,我已经留下了更多的例子:
- PHP convert XML to JSON group when there is one child (Jun 2013)
- 当有一个子节点时,PHP将XML转换为JSON组(2013年6月)
- Resolve namespaces with SimpleXML regardless of structure or namespace (Oct 2014)
- 使用SimpleXML解决名称空间,不管结构或名称空间(2014年10月)
- XML to JSON conversion in PHP SimpleXML (Dec 2014)
- XML到JSON转换的PHP SimpleXML(2014年12月)
Your case I think is similar to what has been asked in the first of those three links.
我认为你的案例与这三个环节中的第一个相似。