从包含重复元素的XML转换PHP数组

时间:2015-08-02 12:36:51

标签: php arrays xml

到目前为止,我一直在使用下面的代码段将XML树转换为数组:

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

..但是,我现在正在使用具有重复键值的XML,因此当它循环遍历XML时,数组就会中断。例如:

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

有没有更好的方法来执行此操作以允许重复键,或者可能是一种在每个键吐出数组时为每个键添加递增值的方法,如下所示:

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

我很难过,所以任何帮助都会非常感激。

3 个答案:

答案 0 :(得分:2)

这是一个完整的通用递归解决方案。

这个类将解析任何结构中的任何XML,无论是否有标记,从最简单的到最复杂的。

它保留所有正确的值并转换它们(bool,txt或int),为所有元素组生成足够的数组键,包括标签,保留重复元素等等......

请原谅静态,它是我使用的大型XML工具集的一部分,在重写所有HHVM或pthread之前,我没有时间正确构建这个,但它将像简单PHP的魅力一样工作。

对于标签,在这种情况下声明的值为'@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);

结果:

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 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 :(得分:0)

你可以循环遍历结果中的每个键,如果值是一个数组(就像你的user那样,你的例子中有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);

答案 2 :(得分:0)

首先,这行代码包含对array的多余演员:

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

当您对 SimpleXMLElement 进行JSON编码时(当参数可以解析为XML时由simplexml_load_string返回),这已经表现为 as-if 那里本来是一个阵列演员。所以最好将其删除:

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

即使结果仍然相同,现在允许您创建 SimpleXMLElement 的子类型,实现JsonSerialize interface更改阵列创建以满足您的需求

blog-series of mine中概述了整体方法(以及默认行为),在Stackoverflow上我已经留下了一些更多的例子:

我认为你的情况类似于这三个链接中的第一个问题。