如何将XML字符串转换为不同结构的PHP数组?多同类型子节点处理遇阻
解决XML转PHP数组时多同类型子节点的问题
我太懂这种头疼的情况了——当XML里存在多个同名字节点时,普通的DOM解析逻辑很容易把它们覆盖成单个值,完全不是我们想要的数组结构。我来帮你重构这个parseXml方法,让它能正确处理多子节点的场景。
问题根源
你的原有方法大概率是直接将每个子节点的键值对赋值到数组中,类似这样:
$array[$node->nodeName] = $this->parseXml($node->C14N());
这种写法在遇到重复节点时,后面的节点会直接覆盖前面的,导致最终数组里只剩最后一个节点的值。
修改后的完整方法
/** * 将XML字符串转换为数组,支持处理多个同类型子节点 * * @param string $xmlString XML字符串 * @return array */ private function parseXml($xmlString) { $doc = new DOMDocument; // 禁用错误提示,避免XML格式不规范时抛出警告 $doc->loadXML($xmlString, LIBXML_NOERROR | LIBXML_NOWARNING); $root = $doc->documentElement; return $this->convertNodeToArray($root); } /** * 递归将DOM节点转换为数组,核心处理多同节点逻辑 * * @param DOMNode $node * @return mixed */ private function convertNodeToArray(DOMNode $node) { $result = []; // 处理文本节点(如果是纯文本内容) if ($node->nodeType === XML_TEXT_NODE) { $value = trim($node->nodeValue); return $value === '' ? null : $value; } // 处理属性 if ($node->hasAttributes()) { foreach ($node->attributes as $attr) { $result['@' . $attr->name] = $attr->value; } } // 处理子节点 if ($node->hasChildNodes()) { foreach ($node->childNodes as $child) { $childValue = $this->convertNodeToArray($child); if ($childValue === null) { continue; } $childName = $child->nodeName; // 关键逻辑:如果该键已存在,转成数组 if (isset($result[$childName])) { // 如果当前不是数组,先把现有值转成数组 if (!is_array($result[$childName])) { $result[$childName] = [$result[$childName]]; } // 添加新的子节点值 $result[$childName][] = $childValue; } else { // 第一次出现,直接赋值 $result[$childName] = $childValue; } } } // 如果结果只有一个子节点且没有属性,直接返回子节点值(简化结构) if (count($result) === 1 && !str_starts_with(key($result), '@')) { return reset($result); } return $result; }
关键改进点
- 新增了
convertNodeToArray递归方法,专门处理节点到数组的转换,逻辑更清晰 - 核心判断:当发现重复的节点键时,自动将其转换为数组类型,把所有同节点的值都存进去
- 处理了文本节点的空值过滤,避免生成无效的空字符串
- 支持XML属性的解析,属性键会以
@开头区分(比如<item id="1">会生成['@id' => '1', ...]) - 可选的简化逻辑:如果节点只有一个子节点且无属性,直接返回子节点值,避免多层嵌套
示例效果
比如你有这样的XML:
<order> <items> <item>Apple</item> <item>Banana</item> <item>Cherry</item> </items> </order>
转换后的数组会是:
[ 'items' => [ 'item' => ['Apple', 'Banana', 'Cherry'] ] ]
完美保留了所有子节点的值。
内容的提问来源于stack exchange,提问作者aborted
相关产品推荐
相关产品推荐

