Xpath - 如何导航到值(Ruby Nokogiri)



如果我想获取货币汇率,比如"USD",给定某个时间,比如"2015-02-09",我会怎么做?

我尝试了以下方法:

/gesmes:Envelope/def:Cube/def:Cube[@time="2014-11-19"]/def:Cube[@currency="USD"]/@rate

虽然我想由于缺乏理解这是错误的,但至少,我知道这是错误的,因为Nokogiri没有运行它。

http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml

编辑:

我将继续猜测我没有正确使用Nokogiri和XPath。

@doc = Nokogiri::XML(File.open("exchange_data.xml"))
@values = @doc.xpath('XPATH HERE')
@values.each {|i| puts i}

我已经阅读了本教程,并设法让它适用于其他 xml 文件,但这个似乎更难破解。

require 'nokogiri'
doc = Nokogiri::XML(File.open("xml4.xml"))
target_date = "2015-02-09"
target_currency = 'USD'
xpaths = [
  "//gesmes:Envelope",
  "/xmlns:Cube",
  "/xmlns:Cube[@time='#{target_date}']",
  "/xmlns:Cube[@currency='#{target_currency}']",
]
xpath = xpaths.join
target_cube = doc.at_xpath(xpath)
puts target_cube.attribute('rate')
--output:--
1.1297

对评论的回应:

您的根标记:

<gesmes:Envelope xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01"
                 xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref">

。用 xmlns 声明两个命名空间,代表 XML 命名空间。 命名空间:

xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01"

声明任何名称以 gesmes 为前缀的子标签,例如:

<gesmes:subject>
  ...
</gesmes:subject>

实际上将有一个标签名称,该名称将指定的 URL 合并到标签名称中,如下所示:

<http://www.gesmes.org/xml/2002-08-01:subject>
  ...
</http://www.gesmes.org/xml/2002-08-01:subject>

您希望使用命名空间的原因是为 Cube 标记创建一个唯一的名称,以便它不会与其他 xml 文档的 Cube 标记冲突。

第二个命名空间声明:

xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref"

默认命名空间声明。 它声明任何未指定前缀的子标记都将在其标记名称中合并指定的 url。 所以像这样的标签:

<Cube>
  ...
</Cube>

变成这样:

<http://www.ecb.int/vocabulary/2002-08-01/eurofxref:Cube>
  ...
</http://www.ecb.int/vocabulary/2002-08-01/eurofxref:Cube>

但是,必须在 xpath 中编写这样的标签名称会很笨拙,因此您可以使用快捷方式代替 url xmlns

/xmlns:Cube

这可能是由于本文档中的命名空间:

<gesmes:Envelope xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01" xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref">

若要检验此假设,请应用以下 XPath 表达式:

/*[local-name() = 'Envelope']/*[local-name() = 'Cube']/*[local-name() = 'Cube'][@time="2014-11-19"]/*[local-name() = 'Cube'][@currency="USD"]/@rate

让我知道你得到了什么。如果您以其他方式正确使用 XPath,则最终应得到:

 rate="1.2535"

如果没有,您没有正确使用Nokogiri的XPath工具,那么您真的需要显示所有Ruby代码才能获得帮助。


编辑

回复评论:

我期待看到一些示例添加到您的答案中,以便我可以了解有关 xml 命名空间的新知识。

7stud 已经给出了正确的答案,我只会添加我认为这个答案中缺少的信息。

显式命名空间

首先,如果元素上显式存在命名空间 URI,则正确的语法使用大括号,无论是前缀命名空间还是默认命名空间:

<{http://www.gesmes.org/xml/2002-08-01}subject>

内部,这就是命名空间在元素上的表示方式(尽管某些应用程序有其他方法将元素与命名空间相关联(。前缀和默认命名空间用于简化此过程。

Nokogiri 中的命名空间

前缀(gesmes:(没有任何固有的含义。它们可以与任意命名空间 URI 相关联,并且每个文档都可以使用 gesmes: 来表示不同的含义。命名空间声明本身不适用于 XPath 引擎 - 通常,如果要在 XPath 表达式中使用前缀,则需要为 XPath 处理器再次声明此命名空间。

然而,Nokogiri 试图通过重新声明在输入文档的根元素上找到的命名空间声明来简化命名空间处理。这很重要,因为它允许您重用在输入的根元素上声明的前缀,而无需实际声明命名空间。对于在根元素上声明的没有前缀的默认命名空间,Nokogiri 定义了一种特殊语法:

xmlns:Cube

文档中存在但在根元素以外的元素上声明的命名空间:

<root>
   <child xmlns:gesmes="http://other.com"/>
</root>

必须在 Nokogiri 中明确声明:

@doc.xpath('//other:Cube', 'other' => 'http://other.com/')

你的原始代码有什么问题?

您的代码:

/gesmes:Envelope/def:Cube/def:Cube[@time="2014-11-19"]/def:Cube[@currency="USD"]/@rate

不起作用,因为您使用的是未知前缀 def: 。此前缀未在输入的根元素上声明,您也没有使用 Nokogiri 声明它。Cube元素位于默认命名空间中,正如我们所看到的,解决它们的正确方法是

/gesmes:Envelope/xmlns:Cube

等等,7stud给了你正确的答案。

相关内容

  • 没有找到相关文章

最新更新