具有默认名称空间绑定的XML上的PHP xpath查询

|| 对于这个问题,我有一个解决方案,但这是一个hack,我想知道是否有更好的方法可以解决此问题。 下面是一个示例XML文件和一个PHP CLI脚本,该脚本执行作为参数给出的xpath查询。对于此测试用例,命令行为:
./xpeg \"//MainType[@ID=123]\"
似乎最奇怪的是这条线,否则我的方法将不起作用:
$result->loadXML($result->saveXML($result));
据我所知,这只是重新解析修改后的XML,在我看来这不是必须的。 有没有更好的方法可以在PHP中对此XML执行xpath查询? 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\"
您可以通过以下方式使它更闪亮 自己找出默认的名称空间(通过查看document元素的namespace属性) 在命令行上支持多个名称空间,并在
$xpath->query()
之前全部注册它们 支持“ 8”形式的参数以创建自定义名称空间前缀 底线是:在XPath中,当您真正表示
//namespace:foo
时,您将无法查询query9ѭ。这些根本不同,因此选择不同的节点。 XML可以定义默认的名称空间(因此可以在文档中删除显式的名称空间使用),但这并不意味着您可以在XPath中删除名称空间使用。     
        出于好奇,如果删除此行会发生什么?
$e->removeAttributeNS($e->getAttributeNode(\"xmlns\")->nodeValue,\"\");
这使我成为最有可能引起您黑客攻击的对象。您基本上是在删除
xmlns=\"http://www.example.com/data\"
部分,然后重新构建DOMDocument。您是否考虑过简单地使用字符串函数来删除该名称空间?
$pieces = explode(\'xmlns=\"\', $xml);
$xml = $pieces[0] . substr($pieces[1], strpos($pieces[1], \'\"\') + 1);
然后继续前进吗?它甚至可能最终变得更快。     
        鉴于XPath语言的当前状态,我认为Tomalek提供了最佳答案:将前缀与默认名称空间关联并为所有标记名称添加前缀。这就是我打算在当前应用程序中使用的解决方案。 如果不可能或不可行,那么比我的hack更好的解决方案是调用一种方法,该方法执行与重新扫描相同的操作(希望更有效):DOMDocument :: normalizeDocument()。该方法的行为是“就像您保存然后加载文档一样,将文档置于“常规”格式。”     
        另外,您可以使用xpath掩码:
//*[local-name(.) = \'MainType\'][@ID=\'123\']
    

要回复问题请先登录注册