我有一个主题问题的解决方案,但它是一个黑客,我想知道是否有更好的方法来做到这一点.
下面是一个示例XML文件和一个PHP CLI脚本,它执行作为参数给出的xpath查询.对于此测试用例,命令行是:
./xpeg "//MainType[@ID=123]"
最奇怪的是这条线,没有它我的方法不起作用:
$result->loadXML($result->saveXML($result));
据我所知,这只是重新解析修改后的XML,在我看来这不应该是必要的.
XML(注意默认命名空间的绑定):
<?xml version="1.0" encoding="utf-8"?> <MyRoot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.com/data http://www.example.com/data/MyRoot.xsd" xmlns="http://www.example.com/data"> <MainType ID="192" comment="Bob's site"> <Price>$0.20</Price> <TheUrl><![CDATA[http://www.example.com/path1/]]></TheUrl> <Validated>N</Validated> </MainType> <MainType ID="123" comment="Test site"> <Price>$99.95</Price> <TheUrl><![CDATA[http://www.example.com/path2]]></TheUrl> <Validated>N</Validated> </MainType> <MainType ID="922" comment="Health Insurance"> <Price>$600.00</Price> <TheUrl><![CDATA[http://www.example.com/eg/xyz.PHP]]></TheUrl> <Validated>N</Validated> </MainType> <MainType ID="389" comment="Used Cars"> <Price>$5000.00</Price> <TheUrl><![CDATA[http://www.example.com/tata.PHP]]></TheUrl> <Validated>N</Validated> </MainType> </MyRoot>
PHP CLI脚本:
#!/usr/bin/PHP-cli <?PHP $xml = file_get_contents("xpeg.xml"); $domdoc = new DOMDocument(); $domdoc->loadXML($xml); // remove the default namespace binding $e = $domdoc->documentElement; $e->removeAttributeNS($e->getAttributeNode("xmlns")->nodeValue,""); // hack hack,cough cough,hack hack $domdoc->loadXML($domdoc->saveXML($domdoc)); $xpath = new DOMXpath($domdoc); $str = trim($argv[1]); $result = $xpath->query($str); if ($result !== FALSE) { dump_dom_levels($result); } else { echo "error\n"; } // The following function isn't really part of the // question. It simply provides a concise summary of // the result. function dump_dom_levels($node,$level = 0) { $class = get_class($node); if ($class == "DOMNodeList") { echo "Level $level ($class): $node->length items\n"; foreach ($node as $child_node) { dump_dom_levels($child_node,$level+1); } } else { $nChildren = 0; foreach ($node->childNodes as $child_node) { if ($child_node->hasChildNodes()) { $nChildren++; } } if ($nChildren) { echo "Level $level ($class): $nChildren children\n"; } foreach ($node->childNodes as $child_node) { if ($child_node->hasChildNodes()) { dump_dom_levels($child_node,$level+1); } } } } ?>
解决方法
解决方案是使用命名空间,而不是摆脱它.
$result = new DOMDocument(); $result->loadXML($xml); $xpath = new DOMXpath($result); $xpath->registerNamespace("x",trim($argv[2])); $str = trim($argv[1]); $result = $xpath->query($str);
并在命令行上将其命名为(请注意XPath表达式中的x:)
./xpeg "//x:MainType[@ID=123]" "http://www.example.com/data"
你可以让它更闪亮
>自己找出默认命名空间(通过查看文档元素的namespace属性)
>在命令行上支持多个命名空间并在$xpath-> query()之前注册它们
>以xyz = http // namespace.uri /的形式支持参数,以创建自定义名称空间前缀
底线是:在XPath中,当你真正的意思是// namespace:foo时,你无法查询// foo.这些根本不同,因此选择不同的节点. XML可以定义默认名称空间(因此可以删除文档中的显式名称空间使用)并不意味着您可以删除XPath中的名称空间使用.