如何使用PHP按a:DESIGN和a:COLOR对XML文件排序?
问题描述
我有一个包含多条条目的XML文件,想要按两个子节点进行排序:先按a:DESIGN排序,再按a:COLOR排序。尝试多种方法后仍无法实现,求帮助。
我的代码
$xml = file_get_contents("http://79.129.20.64:8580/GlxConnector/GetItemsForXml"); $document = new DOMDocument(); $document->loadXml($xml); $xpath = new DOMXpath($document); $list = iterator_to_array($xpath->evaluate('/*/*/*')); uasort( $list, function($one, $two) use ($xpath) { return strcasecmp( $xpath->evaluate('string(a:DESIGN)', $one), $xpath->evaluate('string(a:COLOR)', $two) ); } ); $document->saveXML(); //var_dump($list); $document->save('test1.xml');
示例XML(部分条目)
<GetItemsForXmlResponse xmlns="http://tempuri.org/"> <GetItemsForXmlResult xmlns:a="http://schemas.datacontract.org/2004/07/e_ShopConnector.Entities" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <a:ItemForXML> <a:AVAILABILITY>false</a:AVAILABILITY> <a:CODE>127-0-368-32118-995</a:CODE> <a:COLOR>995</a:COLOR> <a:DESIGN>32118</a:DESIGN> <a:DIMENSION>160X230</a:DIMENSION> <a:DISCOUNT_PERCENTAGE>0.000000000000</a:DISCOUNT_PERCENTAGE> <a:PRICE>121.000000000000</a:PRICE> <a:PRICE_AFTER_DISCOUNT>0.000000000000</a:PRICE_AFTER_DISCOUNT> <a:SERIES>BOHEME</a:SERIES> </a:ItemForXML> <a:ItemForXML> <a:AVAILABILITY>true</a:AVAILABILITY> <a:CODE>126-0-500-22570-795</a:CODE> <a:COLOR>795</a:COLOR> <a:DESIGN>22570</a:DESIGN> <a:DIMENSION>200X250</a:DIMENSION> <a:DISCOUNT_PERCENTAGE>0.000000000000</a:DISCOUNT_PERCENTAGE> <a:PRICE>145.000000000000</a:PRICE> <a:PRICE_AFTER_DISCOUNT>0.000000000000</a:PRICE_AFTER_DISCOUNT> <a:SERIES>SYDNEY</a:SERIES> </a:ItemForXML> <a:ItemForXML> <a:AVAILABILITY>true</a:AVAILABILITY> <a:CODE>162-0-120-32118-995</a:CODE> <a:COLOR>995</a:COLOR> <a:DESIGN>32118</a:DESIGN> <a:DIMENSION>080X150</a:DIMENSION> <a:DISCOUNT_PERCENTAGE>0.000000000000</a:DISCOUNT_PERCENTAGE> <a:PRICE>40.000000000000</a:PRICE> <a:PRICE_AFTER_DISCOUNT>0.000000000000</a:PRICE_AFTER_DISCOUNT> <a:SERIES>DELUXE</a:SERIES> </a:ItemForXML> <a:ItemForXML> <a:AVAILABILITY>true</a:AVAILABILITY> <a:CODE>136-0-252-75002-022</a:CODE> <a:COLOR>022</a:COLOR> <a:DESIGN>75002</a:DESIGN> <a:DIMENSION>140X180</a:DIMENSION> <a:DISCOUNT_PERCENTAGE>0.000000000000</a:DISCOUNT_PERCENTAGE> <a:PRICE>76.000000000000</a:PRICE> <a:PRICE_AFTER_DISCOUNT>0.000000000000</a:PRICE_AFTER_DISCOUNT> <a:SERIES>PANAMA</a:SERIES> </a:ItemForXML> <a:ItemForXML> <a:AVAILABILITY>true</a:AVAILABILITY> <a:CODE>136-0-252-32118-995</a:CODE> <a:COLOR>995</a:COLOR> <a:DESIGN>32118</a:DESIGN> <a:DIMENSION>140X180</a:DIMENSION> <a:DISCOUNT_PERCENTAGE>0.000000000000</a:DISCOUNT_PERCENTAGE> <a:PRICE>76.000000000000</a:PRICE> <a:PRICE_AFTER_DISCOUNT>0.000000000000</a:PRICE_AFTER_DISCOUNT> <a:SERIES>PANAMA</a:SERIES> </a:ItemForXML> </GetItemsForXmlResponse> </GetItemsForXmlResult>
预期输出(按DESIGN和COLOR排序)
<GetItemsForXmlResponse xmlns="http://tempuri.org/"> <GetItemsForXmlResult xmlns:a="http://schemas.datacontract.org/2004/07/e_ShopConnector.Entities" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <a:ItemForXML> <a:AVAILABILITY>true</a:AVAILABILITY> <a:CODE>162-0-120-32118-995</a:CODE> <a:COLOR>995</a:COLOR> <a:DESIGN>32118</a:DESIGN> <a:DIMENSION>080X150</a:DIMENSION> <a:DISCOUNT_PERCENTAGE>0.000000000000</a:DISCOUNT_PERCENTAGE> <a:PRICE>40.000000000000</a:PRICE> <a:PRICE_AFTER_DISCOUNT>0.000000000000</a:PRICE_AFTER_DISCOUNT> <a:SERIES>DELUXE</a:SERIES> </a:ItemForXML> <a:ItemForXML> <a:AVAILABILITY>true</a:AVAILABILITY> <a:CODE>136-0-252-32118-995</a:CODE> <a:COLOR>995</a:COLOR> <a:DESIGN>32118</a:DESIGN> <a:DIMENSION>140X180</a:DIMENSION> <a:DISCOUNT_PERCENTAGE>0.000000000000</a:DISCOUNT_PERCENTAGE> <a:PRICE>76.000000000000</a:PRICE> <a:PRICE_AFTER_DISCOUNT>0.000000000000</a:PRICE_AFTER_DISCOUNT> <a:SERIES>PANAMA</a:SERIES> </a:ItemForXML> <a:ItemForXML> <a:AVAILABILITY>false</a:AVAILABILITY> <a:CODE>127-0-368-32118-995</a:CODE> <a:COLOR>995</a:COLOR> <a:DESIGN>32118</a:DESIGN> <a:DIMENSION>160X230</a:DIMENSION> <a:DISCOUNT_PERCENTAGE>0.000000000000</a:DISCOUNT_PERCENTAGE> <a:PRICE>121.000000000000</a:PRICE> <a:PRICE_AFTER_DISCOUNT>0.000000000000</a:PRICE_AFTER_DISCOUNT> <a:SERIES>BOHEME</a:SERIES> </a:ItemForXML> <a:ItemForXML> <a:AVAILABILITY>true</a:AVAILABILITY> <a:CODE>126-0-500-22570-795</a:CODE> <a:COLOR>795</a:COLOR> <a:DESIGN>22570</a:DESIGN> <a:DIMENSION>200X250</a:DIMENSION> <a:DISCOUNT_PERCENTAGE>0.000000000000</a:DISCOUNT_PERCENTAGE> <a:PRICE>145.000000000000</a:PRICE> <a:PRICE_AFTER_DISCOUNT>0.000000000000</a:PRICE_AFTER_DISCOUNT> <a:SERIES>SYDNEY</a:SERIES> </a:ItemForXML> <a:ItemForXML> <a:AVAILABILITY>true</a:AVAILABILITY> <a:CODE>136-0-252-75002-022</a:CODE> <a:COLOR>022</a:COLOR> <a:DESIGN>75002</a:DESIGN> <a:DIMENSION>140X180</a:DIMENSION> <a:DISCOUNT_PERCENTAGE>0.000000000000</a:DISCOUNT_PERCENTAGE> <a:PRICE>76.000000000000</a:PRICE> <a:PRICE_AFTER_DISCOUNT>0.000000000000</a:PRICE_AFTER_DISCOUNT> <a:SERIES>PANAMA</a:SERIES> </a:ItemForXML> </GetItemsForXmlResponse> </GetItemsForXmlResult>
解决方案
你的代码存在两个核心问题:
- 排序逻辑错误:当前代码把第一个节点的
DESIGN和第二个节点的COLOR做比较,完全不符合先按DESIGN再按COLOR的排序规则。 - 排序后的节点未重新插入DOM:你只是对数组里的节点对象排序,但没有把这些排序后的节点重新放回文档中,所以保存的XML还是原顺序。
修正后的代码如下:
$xml = file_get_contents("http://79.129.20.64:8580/GlxConnector/GetItemsForXml"); $document = new DOMDocument(); $document->loadXml($xml); $xpath = new DOMXpath($document); // 注册命名空间,确保XPath能正确识别a前缀 $xpath->registerNamespace('a', 'http://schemas.datacontract.org/2004/07/e_ShopConnector.Entities'); // 获取所有ItemForXML节点 $items = iterator_to_array($xpath->evaluate('//a:ItemForXML')); // 获取父节点,后续要重新插入排序后的节点 $parent = $xpath->evaluate('//a:ItemForXML[1]/parent::*')[0]; // 正确的排序逻辑:先比较DESIGN,相同再比较COLOR uasort($items, function($one, $two) use ($xpath) { $design1 = $xpath->evaluate('string(a:DESIGN)', $one); $design2 = $xpath->evaluate('string(a:DESIGN)', $two); // 先按DESIGN排序 $designCompare = strcasecmp($design1, $design2); if ($designCompare !== 0) { return $designCompare; } // DESIGN相同则按COLOR排序 $color1 = $xpath->evaluate('string(a:COLOR)', $one); $color2 = $xpath->evaluate('string(a:COLOR)', $two); return strcasecmp($color1, $color2); }); // 先清空父节点下的所有ItemForXML节点 foreach ($items as $item) { $parent->removeChild($item); } // 再按排序后的顺序重新插入 foreach ($items as $item) { $parent->appendChild($item); } // 保存XML $document->save('test1.xml');
关键说明
- 命名空间注册:必须注册
a前缀对应的命名空间,否则XPath无法正确定位到a:DESIGN和a:COLOR节点。 - 排序逻辑修正:先对比两个节点的
DESIGN值,只有当DESIGN相同时,才对比COLOR值,实现多级排序。 - DOM节点重排:排序后的节点需要从父节点移除再重新插入,这样文档的节点顺序才会更新,保存后的XML才是排序后的结果。
内容的提问来源于stack exchange,提问作者Meni
相关产品推荐
相关产品推荐

