关于ruby:Nokogiri / Xpath名称空间查询

Nokogiri/Xpath namespace query

我正在尝试使用xpath提取dc:title元素。 我可以使用以下代码提取元数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
doc = <<END
<?xml version="1.0" encoding="UTF-8"?>
<package xmlns="http://www.idpf.org/2007/opf" version="2.0">
  <metadata xmlns:dc="URI">
    <dc:title>title text</dc:title>
  </metadata>
</package>
END


doc = Nokogiri::XML(doc)

# Awesome this works!
puts '//xmlns:metadata'
puts doc.xpath('//xmlns:metadata')
# => <metadata xmlns:dc="URI"><dc:title>title text</dc:title></metadata>

如您所见,以上内容似乎正常工作。 但是,我似乎无法从该节点树中获取标题信息,以下所有操作均失败。

1
2
3
4
5
6
7
8
puts doc.xpath('//xmlns:metadata/title')
# => nil

puts doc.xpath('//xmlns:metadata/dc:title')
# => ERROR: `evaluate': Undefined namespace prefix

puts doc.xpath('//xmlns:dc:title')
# => ERROR: 'evaluate': Invalid expression: //xmlns:dc:title

有人可以通过上面的xml文档解释如何在xpath中使用名称空间。


解析时需要注册所有名称空间。 Nokogiri自动在根节点上注册名称空间。任何不在根节点上的名称空间都必须自己注册。这应该工作:

1
puts doc.xpath('//dc:title', 'dc' =>"URI")

或者,您可以完全删除名称空间。仅当您确定不会有冲突的节点名称时,才执行此操作。

1
2
doc.remove_namespaces!
puts doc.xpath('//title')


对于'http://www.idpf.org/2007/opf'名称空间URI,使用正确注册的前缀opf'URI'dc,您需要:

1
/*/opf:metadata/dc:title

注意:xmlnsxml是保留的前缀,除了内置的'http://www.w3.org/2000/xmlns/''http://www.w3.org/XML/1998/namespace'之外,不能绑定到任何其他名称空间URI。


作为显式构造名称空间URI的哈希的替代方法,您可以从定义它们的xml元素中检索名称空间定义。

使用您的示例:

1
2
3
4
5
# First grab the metadata node, because that's where"dc" is defined.
metadata = doc.at_xpath('//xmlns:metadata')

# Pass metadata's namespaces as the resolver.
metadata.at_xpath('dc:title', metadata.namespaces)

请注意,第二个xpath也可以是:

1
doc.at_xpath('//dc:title', metadata.namespaces).to_s

但是,当您的祖先较近时,为什么要从根开始搜索呢?另外,您应将定义命名空间的元素及其子元素视为命名空间的"作用域"。搜索有限范围的文件不会造成混乱,并且避免了细微的错误。