【问题标题】:xPath finds nothing but *xPath 只找到 *
【发布时间】:2011-04-12 18:54:09
【问题描述】:

这开始让我很生气。我有这个 XML 代码:

更新为正确的命名空间

<?xml version="1.0" encoding="utf-8"?>

<Infringement xsi:schemaLocation="http://www.movielabs.com/ACNS http://www.movielabs.com/ACNS/ACNS2v1.xsd" xmlns="http://www.movielabs.com/ACNS" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Case>
    <ID>...</ID>
    <Status>Open</Status>
  </Case>
  <Complainant>
    <Entity>...</Entity>
    <Contact>...</Contact>
    <Address>...</Address>
    <Phone>...</Phone>
    <Email>...</Email>
  </Complainant>
  <Service_Provider>
    <Entity>...</Entity>
    <Address></Address>
    <Email>...</Email>
  </Service_Provider>
  <Source>
    <TimeStamp>...</TimeStamp>
    <IP_Address>...</IP_Address>
    <Port>...</Port>
    <DNS_Name></DNS_Name>
    <Type>...</Type>
    <UserName></UserName>
    <Number_Files>1</Number_Files>
    <Deja_Vu>No</Deja_Vu>
  </Source>
  <Content>
    <Item>
      <TimeStamp>...</TimeStamp>
      <Title>...</Title>
      <FileName>...</FileName>
      <FileSize>...</FileSize>
      <URL></URL>
    </Item>
  </Content>
</Infringement>

还有这个 PHP 代码:

<?php 
    $data = urldecode($_POST["xml"]);
    $newXML = simplexml_load_string($data);

    var_dump($newXML->xpath("//ID"));
?>

我只转储了 $newXML 并获得了大量数据,但我运行的唯一一个返回空数组以外的任何内容的 xPath 是“*”

“//ID”不应该找到文档中的所有ID节点吗?为什么它不起作用?

谢谢

【问题讨论】:

  • 问题很可能是命名空间 (xmlns:xsi)。不知道如何进一步提供帮助,技能不足:P
  • 不确定,但请尝试将 xmlns 替换为 ns。 (灵感来自this comment on PHP.net
  • 我可能听起来像个狂热分子,但恕我直言,删除名称空间以使 XPath 查询正常工作只是一种用于克服编程工具缺陷或程序员无能的 XML 破解黑客。命名空间是 XML 中的一个基本概念。任何打算使用 XML 的人都应该学会理解它们。
  • @Lekensteyn,一味地搞乱命名空间就像试图在黑暗中生下豪猪宝宝。为了解析 xsi:schemaLocation 属性,需要 xmlns:xsi 命名空间声明。如果将 xmlns 更改为 ns,XML 文档将不再是格式正确的。您所指的评论是替换 default 命名空间声明 xmlns=... (无冒号或前缀),它确定没有命名空间前缀的元素的命名空间。 @Codemonkey 没有默认命名空间声明,因此该注释不适用。
  • 好的,文档已经被编辑,所以现在它有一个默认的命名空间声明。 @Codemonkey,不要要求我们诊断一个歪曲其内容的文档。

标签: php xml xpath simplexml


【解决方案1】:

您的 XML 文档的根元素似乎具有默认命名空间,其 URI 为“http://www.movielabs.com/ACNS”。这意味着文档中的所有元素都属于该命名空间。问题是所有没有命名空间前缀的 XPath 表达式都在搜索不属于任何命名空间的元素。要从某个命名空间搜索元素(或属性...),您需要将命名空间 URI 注册到某个前缀,然后在 XPath 表达式中使用此前缀。

如果是 PHP 的 simpleXML,它会这样做

$newXML = simplexml_load_string($data);
$newXML->registerXPathNamespace('prefix', 'http://www.movielabs.com/ACNS');
var_dump($newXML->xpath("//prefix:ID"));

prefix实际上可以是任何文本,但命名空间 URI 必须与您的 XML 文档中使用的完全匹配。

【讨论】:

    【解决方案2】:

    我只转储了 $newXML 并得到了 大量数据,但我拥有的唯一 xPath 运行返回任何内容,但 空数组是“*”

    那么var_dump($newXML-&gt;xpath("*"));返回了什么? &lt;Infringement&gt;?

    如果问题是命名空间,试试这个:

    var_dump($newXML->xpath("//*[local-name() = 'ID']"));
    

    这将匹配文档中名称为“ID”的任何元素,无论命名空间如何。

    如果我全部替换,我的东西就可以工作 “xmlns”与“ns”

    等等,什么?您确定向我们展示了文档中所有与 xmlns 相关的属性吗?

    更新: 对问题进行了编辑以表明 XML 确实具有默认命名空间声明。这解释了最初的问题:您的 XPath 表达式选择了不在命名空间中的 ID 元素,但由于默认命名空间声明,您的文档中的元素位于 movielabs ACNS 命名空间中。

    在元素上声明 xmlns="http://www.movielabs.com/ACNS" 意味着“此元素和所有没有命名空间前缀(如 ID)的后代都在命名空间 URI 'http://www.movielabs.com/ACNS' 表示的命名空间中。” (除非介入的后代有不同的默认命名空间声明,这会影响这个。)

    所以使用我上面的local-name() 答案来忽略命名空间,或者使用 jasso 的技术来指定 movielabs ACNS 并按预期使用它。

    【讨论】:

    • local-name() 就是这样。我的脚本将获取大量 XML 文档,我无法确定它们是否都具有相同的默认命名空间
    • @Codemonkey 这是一个很好的解决方案。如果您不知道它们的默认命名空间但它们都在同一个命名空间中(可能使用命名空间前缀),您仍然可以使用 jasso 的方法,因为脚本中的前缀不必与XML 文档。只有命名空间 URI 必须匹配。或者你可以完全忽略命名空间。
    【解决方案3】:

    将此用于任何命名空间:

    var_dump($newXML->xpath("//*:ID"));
    

    【讨论】:

      【解决方案4】:

      我并不精通 PHP 的 XML API,但我怀疑问题出在名称空间上。根据 xpath 方法的工作方式,它可能正在搜索具有空命名空间的 ID 元素。您的 ID 元素从根元素继承其命名空间。

      【讨论】:

      • 我什至不太明白 - 抱歉
      • 是我看错了还是 Infringement 元素上曾经有一个 xmlns 属性?
      • 有,是的。实际上有两个。如果我用“ns”替换所有“xmlns”,我的东西就可以工作,但是没有办法更改 XML?
      • @Codemonkey:曾经有两个 xmlns属性?我现在看到一个,xmlns:xsi。当前问题是否显示了&lt;Infringement&gt; 元素的实际属性?这对于诊断问题至关重要。
      【解决方案5】:

      您在文档元素中定义了一个 xml 命名空间(xmlns="http://www.movielabs.com/ACNS" 属性)。命名空间是 URL http://www.movielabs.com/ACNS。这必须通过一个全局唯一的字符串(一个 URN)。因此,经常使用 URL。有人将您的域用于命名空间的可能性非常低,您可以在 URL 中放置一些文档。

      XML 解析器解析命名空间。该节点有 4 个属性。

      对于&lt;Infringement xmlns="http://www.movielabs.com/ACNS"/&gt;

      $namespaceURI => http://www.movielabs.com/ACNS
      $localName => Infringement
      $prefix => 
      $nodeName => Infringement
      

      对于&lt;movie:Infringement xmlns:movie="http://www.movielabs.com/ACNS"/&gt;

      $namespaceURI => http://www.movielabs.com/ACNS
      $localName => Infringement
      $prefix => movie
      $nodeName => movie:Infringement
      

      $namespaceURI$localName 是稳定的。其他两个取决于前缀。前缀是命名空间的别名。命名空间 uri 又长又复杂,如果在每个元素/属性上使用它会使 XML 更难于阅读和编写。但是您可以将元素节点解释为:

      {http://www.movielabs.com/ACNS}:Infringement
      

      所以命名空间是定义节点含义的一件事,而不是前缀/别名。前缀可以在子元素上重新定义。

      <foo xmlns="urn:foo"><bar xmlns="urn:bar"/></foo>
      

      Xpath 使用与自己的解析器相同的概念。您为命名空间注册自己的前缀。所以在 XML 中如何使用前缀并不重要,只有命名空间 uri 必须匹配。

      在 DOM 中,您在 DOMXPath 实例上执行此操作:

      $dom = new DOMDocument();
      $dom->loadXml($xml);
      $xpath = new DOMXpath($dom);
      $xpath->registerNamespace('movie', 'http://www.movielabs.com/ACNS');
      
      var_dump(
        $xpath->evaluate('string(/movie:Infringement/movie:Case/movie:ID)')
      );
      

      在 SimpleXML 中,您可以在 SimpleXMLElement 上注册命名空间。

      $element = simplexml_load_string($xml);
      $element->registerXpathNamespace('movie', 'http://www.movielabs.com/ACNS');
      var_dump(
        (string)$element->xpath('/movie:Infringement/movie:Case/movie:ID')[0]
      );
      

      提示:默认命名空间仅用于元素,属性在“no/empty namespace”中,除非它们有前缀。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-06
        • 2020-11-15
        • 2019-05-25
        • 2014-11-07
        • 2015-12-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多