Nokogiri 未在 ruby 中解析 XML - xmlns 问题?

Nokogiri not parsing XML in ruby - xmlns issue?

给定以下 ruby 代码:

require 'nokogiri'

xml = "<?xml version='1.0' encoding='UTF-8'?>
<ProgramList xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns='http://publisher.webservices.affili.net/'>
  <TotalRecords>145</TotalRecords>
  <Programs>
    <ProgramSummary>
      <ProgramID>6540</ProgramID>
      <Title>Matalan</Title>
      <Limitations>A bit of text
      </Limitations>
      <URL>http://www.matalan.co.uk</URL>
      <ScreenshotURL>http://www.matalan.co.uk/</ScreenshotURL>
      <LaunchDate>2009-11-02T00:00:00</LaunchDate>
      <Status>1</Status>
    </ProgramSummary>
    <ProgramSummary>
      <ProgramID>11787</ProgramID>
      <Title>Club 18-30</Title>
      <Limitations/>
      <URL>http://www.club18-30.com/</URL>
      <ScreenshotURL>http://www.club18-30.com</ScreenshotURL>
      <LaunchDate>2013-05-16T00:00:00</LaunchDate>
      <Status>1</Status>
    </ProgramSummary>
  </Programs>
</ProgramList>"

doc = Nokogiri::XML(xml)
p doc.xpath("//Programs")

给出:

=> []

不是预期的那样。

如果我从最初的 <ProgramList> 标签中删除 xmlns='http://publisher.webservices.affili.net/',我会得到预期的输出。

确实,如果我将 xmlns='http://publisher.webservices.affili.net/' 更改为 xmlns:anything='http://publisher.webservices.affili.net/',我会得到预期的输出。

所以我的问题是这里发生了什么?这是畸形的 XML 吗?处理它的最佳策略是什么?

虽然在此示例中进行了硬编码,但 XML 是(将)来自 Web 服务。

更新

我知道我可以使用 remove_namespaces! 方法,但 Nokogiri 文档确实说 "...probably is not a good thing in general" 可以做到这一点。我也对为什么会这样以及 'correct' XML 应该是什么感兴趣。

xmlns='http://publisher.webservices.affili.net/'表示其出现的元素下的所有元素(包括元素本身)的default namespace。这意味着所有没有显式命名空间的元素都属于这个命名空间。

XPath 查询没有默认的命名空间(至少在 XPath 1.0 中是这样),因此在没有前缀的情况下出现的任何名称都指的是没有命名空间的元素。

在您的代码中,您希望在 http://publisher.webservices.affili.net/ 命名空间(因为这是默认命名空间)中查找 Program 元素,但正在寻找(在您的 XPath 查询中)Program 命名空间中的元素。

要显式specify the namespace in the query,你可以这样做:

doc.xpath("//pub:Programs", "pub" => "http://publisher.webservices.affili.net/")

Nokogiri 使在根元素上声明的名称空间(如本例)更容易一些,使用相同的前缀为您声明它们。它还将使用 xmlns 前缀声明默认命名空间,因此您也可以这样做:

doc.xpath("//xmlns:Programs")

这会给你相同的结果。