使用 PHP 将 XML 转换为关联数组
Posted
技术标签:
【中文标题】使用 PHP 将 XML 转换为关联数组【英文标题】:XML into Associative Array using PHP 【发布时间】:2011-09-25 21:38:57 【问题描述】:任何人都可以帮助将数据从 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'
因此 [Computer-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";
【问题讨论】:
你能贴出给出错误输出的代码吗? 使用 XML 库会有所帮助。 php.net/manual/en/refs.xml.php 您能解释一下您为什么要这样做吗?为什么不能使用 DOM 或 SimpleXml 提供的树结构? 【参考方案1】:这对我很有效,而且很简单。
$ob = simplexml_load_file('test.xml');
$json = json_encode($ob);
$array = json_decode($json, true);
【讨论】:
效果很好,但使用 CDATA 失败。为了支持 CDATA,请参阅此过滤器:php.net/manual/en/function.simplexml-load-string.php#82686【参考方案2】:一个例子可能是:
$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
)
这并不完全是您想要的,但它是一个开始,可以轻松调整以提供不同的结果。
【讨论】:
【参考方案3】:这是我生成关联数组的函数,派生自
Recursive cast from SimpleXMLObject to Array
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
)
)
)
)
)
)
)
【讨论】:
【参考方案4】:将 xml 读入一个 DOM 对象,循环遍历它,将结果保存到一个数组中。就这么简单吗?
【讨论】:
相对于该页面上的其他帖子而言,这是低价值的。看起来更像是评论/提示而不是答案。【参考方案5】:简单的数组可能是二维的,但多维数组可以很容易地复制像 xml 这样的层次结构。
Google 'associative multi-dimensional array 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";
等等……
【讨论】:
【参考方案6】:我修改了 user655000 的答案,使其更接近 json_decode(json_encode($dom)) 格式化/返回数据的方式。我还将初始数组参数设为可选,因为无论如何它都会为空。
我无法使用 decode(encode) 方法,因为 PHP 的 encode 函数中似乎存在错误,这导致 decode() 在某些示例数据上返回 null。我尝试了一个更安全的编码函数版本,但内存不足。
存在细微的行为差异。如果存在 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;
【讨论】:
以上是关于使用 PHP 将 XML 转换为关联数组的主要内容,如果未能解决你的问题,请参考以下文章
php Laravel Collection将数组转换为关联数组