通过PHP按SKU属性合并多个同结构XML文件
问题
我有3个结构相同的XML文件,需要通过PHP按productsku属性合并,规则如下:
- 相同
productsku的<product>元素合并属性:保留已有属性,补充缺失的属性 - 目标文件中不存在的
<product>元素直接添加
现有代码仅对数字类型SKU有效,且只能处理2个文件,无法适配我的非数字SKU和3个文件的需求,请求提供可行的PHP代码片段。
示例文件
1.xml
<data> <product productsku="ABC1" price="550000" pricesale="" hn="1"></product> <product productsku="ABC2" price="" pricesale="" hn="0"></product> </data>
2.xml
<data> <product productsku="ABC2" price="550000" pricesale="" dn="2"></product> <product productsku="ABC3" price="" pricesale="" dn="0"></product> </data>
3.xml
<data> <product productsku="ABC3" price="550000" pricesale="" gn="3"></product> <product productsku="ABC4" price="" pricesale="" gn="0"></product> </data>
期望合并结果
<data><product productsku="ABC1" price="550000" pricesale="" hn="1"></product> <product productsku="ABC2" price="550000" pricesale="" dn="2" hn="0"></product> <product productsku="ABC3" price="550000" pricesale="" dn="0" gn="3"></product> <product productsku="ABC4" price="" pricesale="" gn="0"></product> </data>
现有尝试代码
<?php $xml1 = file_get_contents('1.xml'); $xml2 = file_get_contents('2.xml'); $targetDom = new DOMDocument(); $targetDom->loadXml($xml1); $targetXpath = new DOMXpath($targetDom); $addDom = new DOMDocument(); $addDom->loadXml($xml2); $addXpath = new DOMXpath($addDom); // merge attributes of product elements depending on productsku foreach ($targetXpath->evaluate('//product[@productsku]') as $product) { $productsku = $product->getAttribute('productsku'); foreach ($addXpath->evaluate('//product[@productsku='.$productsku.']/@*') as $attribute) { if (!$product->hasAttribute($attribute->name)) { $product->setAttribute($attribute->name, $attribute->value); } } } // copy products elements that are not in target dom foreach ($addXpath->evaluate('//product[@productsku]') as $product) { $productsku = $product->getAttribute('productsku'); if ($targetXpath->evaluate('count(//product[@productsku='.$productsku.'])') == 0) { $targetDom->documentElement->appendChild( $targetDom->importNode($product) ); } } echo $targetDom->saveXml();
解决方案
现有代码的核心问题有两个:
- XPath查询中,非数字类型的SKU没有用引号包裹,导致XPath语法错误
- 仅硬编码处理2个文件,无法扩展到3个及以上文件
以下是修正后的代码,支持任意数量的XML文件和非数字SKU:
<?php // 定义需要合并的所有XML文件路径 $xmlFiles = ['1.xml', '2.xml', '3.xml']; // 初始化目标DOM,以第一个文件为基础 $targetDom = new DOMDocument(); $targetDom->load($xmlFiles[0]); $targetXpath = new DOMXPath($targetDom); // 循环处理剩余的XML文件 for ($i = 1; $i < count($xmlFiles); $i++) { $currentFile = $xmlFiles[$i]; $addDom = new DOMDocument(); $addDom->load($currentFile); $addXpath = new DOMXPath($addDom); // 1. 合并已有SKU的属性 foreach ($addXpath->evaluate('//product[@productsku]') as $sourceProduct) { $sku = $sourceProduct->getAttribute('productsku'); // 使用XPath变量绑定,避免字符串拼接的语法问题,同时更安全 $targetProducts = $targetXpath->evaluate('//product[@productsku = $sku]', null, ['sku' => $sku]); if ($targetProducts->length > 0) { $targetProduct = $targetProducts->item(0); // 遍历源产品的所有属性,补充目标产品缺失的属性 foreach ($sourceProduct->attributes as $attr) { if (!$targetProduct->hasAttribute($attr->name)) { $targetProduct->setAttribute($attr->name, $attr->value); } } } else { // 2. 目标中不存在的SKU,直接导入节点 $importedNode = $targetDom->importNode($sourceProduct, true); $targetDom->documentElement->appendChild($importedNode); } } } // 输出合并后的XML(可替换为保存到文件) $targetDom->formatOutput = true; // 可选:格式化输出,便于阅读 echo $targetDom->saveXML(); // 若要保存到文件,使用:$targetDom->save('merged.xml');
代码说明
- 批量处理文件:通过数组定义所有待合并的XML文件,循环处理,支持任意数量的文件
- 安全的XPath查询:使用XPath变量绑定
$sku,避免非数字SKU因未加引号导致的语法错误,同时防止注入风险 - 合并逻辑优化:遍历源文件的产品,先检查目标中是否存在相同SKU,存在则补充属性,不存在则直接导入,逻辑更清晰
- 可选格式化:开启
formatOutput可让输出的XML更易读,按需使用
内容的提问来源于stack exchange,提问作者Phan Vũ
相关产品推荐
相关产品推荐

