具有默认名称空间绑定的XML上的PHP xpath查询 [英] PHP xpath query on XML with default namespace binding

查看:89
本文介绍了具有默认名称空间绑定的XML上的PHP xpath查询的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对这个问题有一个解决方案,但这是一个hack,我想知道是否有更好的方法可以做到这一点.

I have one solution to the subject problem, but it’s a hack and I’m wondering if there’s a better way to do this.

下面是一个示例XML文件和一个PHP CLI脚本,该脚本执行作为参数给出的xpath查询.对于此测试用例,命令行为:

Below is a sample XML file and a PHP CLI script that executes an xpath query given as an argument. For this test case, the command line is:

./xpeg "//MainType[@ID=123]"

最奇怪的是这条线,没有这条线我的方法将行不通:

What seems most strange is this line, without which my approach doesn’t work:

$result->loadXML($result->saveXML($result));

据我所知,这只是重新解析了修改后的XML,在我看来这不是必须的.

As far as I know, this simply re-parses the modified XML, and it seems to me that this shouldn’t be necessary.

是否有更好的方法在PHP中对此XML执行xpath查询?

Is there a better way to perform xpath queries on this XML in PHP?

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脚本:


PHP CLI Script:

#!/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);
      }
    }
  }
}
?>

推荐答案

解决方案是使用名称空间,而不是摆脱它.

The solution is using the namespace, not getting rid of it.

$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:)

And call it as this on the command line (note the x: in the XPath expression)

./xpeg "//x:MainType[@ID=123]" "http://www.example.com/data"

您可以通过以下方式使它更加闪亮

You can make this more shiny by

  • 自己找出默认的名称空间(通过查看document元素的namespace属性)
  • 在命令行上支持多个名称空间,并在$xpath->query()
  • 之前全部注册它们
  • xyz=http//namespace.uri/形式的支持参数以创建自定义名称空间前缀
  • finding out default namespaces yourself (by looking at the namespace property of the document element)
  • supporting more than one namespace on the command line and register them all before $xpath->query()
  • supporting arguments in the form of xyz=http//namespace.uri/ to create custom namespace prefixes

最底线是:在XPath中,当您真正指的是//namespace:foo时,您无法查询//foo.这些根本不同,因此选择不同的节点. XML可以定义默认的名称空间(从而可以在文档中删除显式名称空间使用)这一事实并不意味着您可以在XPath中删除名称空间使用.

Bottom line is: In XPath you can't query //foo when you really mean //namespace:foo. These are fundamentally different and therefore select different nodes. The fact that XML can have a default namespace defined (and thus can drop explicit namespace usage in the document) does not mean you can drop namespace usage in XPath.

这篇关于具有默认名称空间绑定的XML上的PHP xpath查询的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆