使用命名空间对属性进行 XPath 过滤

Posted

技术标签:

【中文标题】使用命名空间对属性进行 XPath 过滤【英文标题】:XPath filtering on attribute with namespace 【发布时间】:2011-10-20 05:19:33 【问题描述】:

我需要创建 XPath 表达式以根据给定命名空间中的属性进行过滤。示例 XML 是:

<feed xmlns='http://www.w3.org/2005/Atom' xmlns:media='http://search.yahoo.com/mrss/'
  xmlns:yt='http://gdata.youtube.com/schemas/2007'> ...
 <entry>
  <media:group>
   <media:thumbnail url='http://i.ytimg.com/1.jpg' yt:name='default'/>
   <media:thumbnail url='http://i.ytimg.com/2.jpg' yt:name='hqdefault'/>
   <media:thumbnail url='http://i.ytimg.com/3.jpg' yt:name='start'/>
   <media:thumbnail url='http://i.ytimg.com/4.jpg' yt:name='middle'/>
  </media:group>
 </entry>

我需要获取属性 yt:name 设置为 'hqdefault' 的节点的 url。

我尝试了 XPath 表达式

'./media:group/media:thumbnail[@yt:name='hqdefault']/@url'

但似乎用 yt:name 指定命名空间属性不起作用。进行查询时,我得到一个空的 DOMNodeList。

我在 php 中访问 XML,所以我注册了 yt 命名空间:

registerNamespace( 'yt', 'http://gdata.youtube.com/schemas/2007' );

提前谢谢

【问题讨论】:

“不起作用” - 你需要说出发生了什么。错误信息? 【参考方案1】:

假设其余的都按顺序进行,只需将 xpath 中的第一个 . 替换为 / 即可获得 //media:group/...(或以 /atom:feed/media:group/... 开头并注册 atom 命名空间)。

这是一个完整的工作示例:

<?php
$dom = new DOMDocument();
$dom->loadXML( <<<XML
<feed xmlns='http://www.w3.org/2005/Atom' xmlns:media='http://search.yahoo.com/mrss/'
  xmlns:yt='http://gdata.youtube.com/schemas/2007'>
  <entry>
    <media:group>
      <media:thumbnail url='http://i.ytimg.com/1.jpg' yt:name='default'/>
      <media:thumbnail url='http://i.ytimg.com/2.jpg' yt:name='hqdefault'/>
      <media:thumbnail url='http://i.ytimg.com/3.jpg' yt:name='start'/>
      <media:thumbnail url='http://i.ytimg.com/4.jpg' yt:name='middle'/>
    </media:group>
  </entry>
</feed>
XML
);

$x = new DOMXPath( $dom );
$x->registerNamespace( 'yt', 'http://gdata.youtube.com/schemas/2007' );
$x->registerNamespace( 'media', 'http://search.yahoo.com/mrss/' );
$l= $x->query( "//media:group/media:thumbnail[@yt:name='hqdefault']/@url" );
for ($i=0; $i<$l->length; $i++) var_dump( $l->item($i)->value );

【讨论】:

【参考方案2】:

XPath 看起来是正确的。

可能是您的库不支持命名空间属性,或者您没有正确注册 yt 命名空间和/或 media 命名空间。

尝试只匹配谓词过滤器内的local-name()namespace-uri(),而不是使用命名空间前缀:

./*[local-name()='group'
      and namespace-uri()='http://search.yahoo.com/mrss/'
    ]/*[local-name()='thumbnail'
         and namespace-uri()='http://search.yahoo.com/mrss/'
         and @*[local-name()='name'
                 and namespace-uri()='http://gdata.youtube.com/schemas/2007'
                 and .='hqdefault'
                ]
        ]/@url

如果可行,那么为这些命名空间前缀注册命名空间时会出现问题。

【讨论】:

【参考方案3】:

yt 命名空间前缀在您的示例 XML 中使用,但未声明。如果该示例 XML 确实存在,那么它不是格式良好的 XML(在命名空间方面)。因此,没有通用 XML 工具(例如 XSLT)可能能够处理它。

另一方面,如果在您的源文档中某处声明了 yt 命名空间前缀但您没有向我们展示,那么您需要在您的 XPath 处理环境(我猜是 XSLT)中声明一个前缀相同的命名空间 URI。例如

<xsl:stylesheet ... xmlns:yt="theNamespaceURIForYT">

XPath 不知道源文档中出现的任何名称空间前缀声明。它只知道每个元素(和属性)所属的命名空间(URI)。

【讨论】:

我没有输入整个 XML,抱歉。它是 YouTube 播放列表导出 XML,它以:&lt;feed xmlns='http://www.w3.org/2005/Atom' xmlns:media='http://search.yahoo.com/mrss/' xmlns:openSearch='http://a9.com/-/spec/opensearch/1.1/' xmlns:gd='http://schemas.google.com/g/2005' xmlns:yt='http://gdata.youtube.com/schemas/2007' gd:etag='W/&amp;quot;C04ESH47eCp7ImA9WhZWGUk.&amp;quot;'&gt; 开头,我在 php 中访问 XML,所以我注册了 yt 命名空间:registerNamespace( 'yt', 'http://gdata.youtube.com/schemas/2007' ); 在进行查询时我得到一个空的 DOMNodeList 是行不通的。谢谢

以上是关于使用命名空间对属性进行 XPath 过滤的主要内容,如果未能解决你的问题,请参考以下文章

Java XPath:使用默认命名空间 xmlns 进行查询

XPATHS 和默认命名空间

XPATHS和默认命名空间

如何使用 XPath 忽略命名空间

为啥命名空间限定节点没有 XPath 语法?

如何使用Xpath检索XML文件中的命名空间