使用PHP将XML转换为关联数组

9
有人能帮忙将XML文档中的数据转换为关联数组吗?由于XML结构有点类似于3D,而数组更像是2D结构(请原谅我在整个过程中缺乏正确的术语)。XML元素具有属性、子元素和孙子元素(但我不知道它们的名称),因此我想尝试将数组中的键制作成每个子元素/属性名称的连接,并将值设置为该值。问题是我需要将属性名称和值作为连接数组键的一部分,以使其唯一...
例如:
<Computer id="1">   
    <OS>
        <Name>Linux</Name>
        <Age>Older than me</Age>
    </OS>
</Computer>
<Computer id="2">
    <OS>
        <Name>Windows</Name>
        <Age>Not so much</Age>
    </OS>
</Computer>

最好能够提供:

[Computer-id-1-OS-Name] = 'Linux'
[Computer-id-1-OS-Age] = 'Older than me'
[Computer-id-2-OS-Name] = 'Windows'
[Computer-id-2-OS-Age] = 'Not so much'

但是我得到了这个结果:
[Computer-id] = '1'
[Computer-OS-Name] = 'Linux'
[Computer-OS-Age] = 'Older than me'
[Computer-id] = '2'
[Computer-OS-Name] = 'Windows'
[Computer-OS-Age] = 'Not so much'

所以[计算机-id]密钥不是唯一的。我正在使用递归函数读取这些值,但我无法确定如何将属性名称和属性值放入下级键的名称中...(顺便说一句,这似乎是一个不合逻辑的任务,但有很好的理由!)任何帮助都将不胜感激...

以下是将XML数据读入多维数组后“压缩”数据的功能。我不确定我的方法是否正确!

function flattenArray ($array, $baseName = NULL)
{
    reset($array);
    while (list ($key, $value) = each($array)) {
        $outKey = $key . "-";
        if (is_array($value)) {
            flattenArray($value, $baseName . $outKey);
        } else {
            $finalKey = $baseName . rtrim($outKey, '-');
            $finalValue = $value;
            echo "$finalKey = $finalValue\n";
        }
    }
}

1
你能发一下导致输出不正确的代码吗? - shanethehat
1
使用XML库可能会有所帮助。http://www.php.net/manual/en/refs.xml.php - Sinan
请问您能否解释一下为什么您想这样做?为什么不能使用DOM或SimpleXml提供的树形结构呢? - Gordon
6个回答

45

这对我来说非常有效,而且很简单。

$ob = simplexml_load_file('test.xml');
$json = json_encode($ob);
$array = json_decode($json, true);

3
它的表现非常好,但在处理 CDATA 时会出错。为了支持 CDATA,请参考此过滤器:http://php.net/manual/en/function.simplexml-load-string.php#82686 - Andrea Lazzarotto

5
一个例子可以是:
$dom = new DOMDocument;
$dom->loadXML(
    '<root>
        <Computer id="1">   
            <OS>
                <Name>Linux</Name>
                <Age>Older than me</Age>
            </OS>
        </Computer>

        <Computer id="2">
            <OS>
                <Name>Windows</Name>
                <Age>Not so much</Age>
            </OS>
        </Computer>
    </root>'
);

$xpath = new DOMXPath($dom);
$result = array();

foreach ($xpath->query('//*[count(*) = 0]') as $node) {
    $path = array();
    $val = $node->nodeValue;

    do {
        if ($node->hasAttributes()) {
            foreach ($node->attributes as $attribute) {
                $path[] = sprintf('%s[%s]', $attribute->nodeName, $attribute->nodeValue);
            }
        }
        $path[] = $node->nodeName;
    }
    while ($node = $node->parentNode);

    $result[implode('/', array_reverse($path))] = $val;
}

print_r($result);

输出:

Array
(
    [#document/root/Computer/id[1]/OS/Name] => Linux
    [#document/root/Computer/id[1]/OS/Age] => Older than me
    [#document/root/Computer/id[2]/OS/Name] => Windows
    [#document/root/Computer/id[2]/OS/Age] => Not so much
)

这可能并不完全符合您的要求,但它是一个起点,可以轻松地进行调整以获得不同的结果。


5
这是用于生成关联数组的函数,源自 SimpleXMLObject 到 数组的递归转换
function xml2assoc($obj, &$arr) {
  $children = $obj->children();
  foreach ( $children as $elementName => $node ) {

    if (!isset($arr[$elementName])) {
      $arr[$elementName] = array();
    }
    $temp = array();
    $attributes = $node->attributes();
    foreach ( $attributes as $attributeName => $attributeValue ) {
      $attribName = strtolower(trim((string) $attributeName));
      $attribVal = trim((string) $attributeValue);
      $temp[$attribName] = $attribVal;
    }
    $text = (string) $node;
    $text = trim($text);
    if (strlen($text) > 0) {
      $temp ['text='] = $text;
    }
    $arr[$elementName][] = $temp;
    $nextIdx = count($arr[$elementName]);
    xml2assoc($node, $arr[$elementName][$nextIdx - 1]);
  }
  return;
}

$xml = '<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[news]]></MsgType>
<ArticleCount>2</ArticleCount>
<Articles>
<item>
<Title><![CDATA[title1]]></Title> 
<Description><![CDATA[description1]]></Description>
<PicUrl><![CDATA[picurl]]></PicUrl>
<Url><![CDATA[url]]></Url>
</item>
<item>
<Title><![CDATA[title]]></Title>
<Description><![CDATA[description]]></Description>
<PicUrl><![CDATA[picurl]]></PicUrl>
<Url><![CDATA[url]]></Url>
</item>
</Articles>
</xml> ';

$dom = new SimpleXMLElement($xml);

$arr = array();

xml2assoc($dom, $arr);
print_r($arr);

生成的数组:

Array
(
    [ToUserName] => Array
        (
            [0] => Array
                (
                    [text=] => toUser
                )

        )

    [FromUserName] => Array
        (
            [0] => Array
                (
                    [text=] => fromUser
                )

        )

    [CreateTime] => Array
        (
            [0] => Array
                (
                    [text=] => 12345678
                )

        )

    [MsgType] => Array
        (
            [0] => Array
                (
                    [text=] => news
                )

        )

    [ArticleCount] => Array
        (
            [0] => Array
                (
                    [text=] => 2
                )

        )

    [Articles] => Array
        (
            [0] => Array
                (
                    [item] => Array
                        (
                            [0] => Array
                                (
                                    [Title] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [text=] => title1
                                                )

                                        )

                                    [Description] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [text=] => description1
                                                )

                                        )

                                    [PicUrl] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [text=] => picurl
                                                )

                                        )

                                    [Url] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [text=] => url
                                                )

                                        )

                                )

                            [1] => Array
                                (
                                    [Title] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [text=] => title
                                                )

                                        )

                                    [Description] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [text=] => description
                                                )

                                        )

                                    [PicUrl] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [text=] => picurl
                                                )

                                        )

                                    [Url] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [text=] => url
                                                )

                                        )

                                )

                        )

                )

        )

)

3
将xml读入DOM对象中,循环遍历它,将结果保存到数组中。就这么简单。

这篇文章相对于页面上的其他帖子来说价值较低。看起来更像是一条评论/提示,而不是一个答案。 - mickmackusa

1
简单数组可能是2D的,但多维数组可以非常轻松地复制像XML这样的分层结构。
请Google“php关联多维数组”以获取更多信息。
然而,正如已经说明的那样,PHP具有内置的XML解析器,因此根本没有必要在数组中重新创建XML,更不用说将其扁平化为简单数组了。
在PHP中,您的数组结构应该类似于这样:
$computers["computers"]["computer-1"]["OS"]["Name"] = "Linux";
$computers["computers"]["computer-1"]["OS"]["Age"] = "Older Than Me";

$computers["computers"]["computer-2"]["OS"]["Name"] = "Windows";
$computers["computers"]["computer-2"]["OS"]["Age"] = "Not so much";

等等...


0

我修改了user655000的答案,使其更接近json_decode(json_encode($dom))格式/返回数据的方式。我还将初始数组参数设为可选项,因为它无论如何都会是空的。

由于PHP的encode函数存在错误,导致decode()在某些示例数据上返回null,因此我无法使用decode(encode)方法。我尝试了一个更安全的encode函数版本,但它耗尽了内存。

有一个轻微的行为差异。如果存在nodeText,则decode(encode)方法将丢弃任何属性(可能也包括子元素)。而我的方法则不会。

function readxml($xmlfile, $recursive = false){
    $ob = simplexml_load_file($xmlfile);
    //primary method
    $json = json_encode($ob);
    $array = json_decode($json, true);
    if(is_null($array)){//backup method
        $array = xml2assoc($ob);
    }
    return $array;
}

function xml2assoc($obj, &$arr = null) {
    $children = $obj->children();//->count(); 
    $nodes = [];
    foreach ( $children as $elementName => $node ) {
        if(!isset($nodes[$elementName])){
            $nodes[$elementName] = 0;
        }
        $nodes[$elementName]++;
    }
    $indexes = [];

    if($arr === null){
        $arr = [];
    }
    foreach ( $children as $elementName => $node ) {
        $temp = array();
        $grandchildren = $node->children()->count();
        
        //attributes        
        $attributes = $node->attributes();
        foreach ( $attributes as $attributeName => $attributeValue ) {
            $attribName = trim((string) $attributeName);
            $attribVal = trim((string) $attributeValue);
            $temp["@attributes"][$attribName] = $attribVal;
        }
        
        //text      
        $text = (string) $node;
        $text = trim($text);
        if (strlen($text) > 0) {
            if(count($temp) == 0 && $grandchildren == 0){
                $temp = $text;//discard the children/attribute data since there aren't any
            } else {
                $temp["NodeText"] = $text;//retain the children/attributes
            }
        }       
        
        //grandchildren
        if($temp || is_string($temp) || $grandchildren > 0 ){
            if( $nodes[$elementName] == 1 ){//only one of it's kind
                $arr[$elementName] = $temp;
                xml2assoc($node, $arr[$elementName]);
            } else {//has multiple nodes of the same kind
                if(isset($indexes[$elementName])){
                    $indexes[$elementName]++;
                } else {
                    $indexes[$elementName] = 0;
                }
                $index = $indexes[$elementName];
                $arr[$elementName][$index] = $temp;
                xml2assoc($node, $arr[$elementName][$index]);
            }
        }
    }
    return $arr;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接