如何在带有前缀但没有名称空间的节点上使用xpath?

Bor*_*era 5 ruby xml xpath namespaces nokogiri

我有一个我需要解析的XML文件。我无法控制文件的格式,无法更改它。

该文件使用前缀(称为a),但未在任何地方为该前缀定义名称空间。我似乎无法xpath用来查询具有a名称空间的节点。

这是xml文档的内容

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

<a:root>
  <a:thing>stuff0</a:thing>
  <a:thing>stuff1</a:thing>
  <a:thing>stuff2</a:thing>
  <a:thing>stuff3</a:thing>
  <a:thing>stuff4</a:thing>
  <a:thing>stuff5</a:thing>
  <a:thing>stuff6</a:thing>
  <a:thing>stuff7</a:thing>
  <a:thing>stuff8</a:thing>
  <a:thing>stuff9</a:thing>
</a:root>
Run Code Online (Sandbox Code Playgroud)

我正在使用Nokogiri查询文档:

doc = Nokogiri::XML(open('text.xml'))
things = doc.xpath('//a:thing')
Run Code Online (Sandbox Code Playgroud)

失败,出现以下错误:

Nokogiri::XML::XPath::SyntaxError: Undefined namespace prefix: //a:thing
Run Code Online (Sandbox Code Playgroud)

从我的研究中,我发现可以在xpath方法中为前缀指定名称空间:

things = doc.xpath('//a:thing', a: 'nobody knows')
Run Code Online (Sandbox Code Playgroud)

这将返回一个空数组。

对我来说,获得所需节点的最佳方法是什么?

Jus*_* Ko 5

问题是在XML文档中没有正确定义名称空间。结果,Nokogiri认为节点名称是“ a:root”,而不是“ a”是名称空间,而“ root”是节点名称:

xml = %Q{
    <?xml version="1.0" encoding="UTF-8"?>
    <a:root>
      <a:thing>stuff0</a:thing>
      <a:thing>stuff1</a:thing>
    </a:root>
}
doc = Nokogiri::XML(xml)
puts doc.at_xpath('*').node_name
#=> "a:root"
puts doc.at_xpath('*').namespace
#=> ""
Run Code Online (Sandbox Code Playgroud)

解决方案1-用冒号指定节点名称

一种解决方案是搜索名称为“ a:thing”的节点。//a:thing因为XPath会将“ a”视为命名空间,所以您不能这样做。您可以通过以下方法解决此问题//*[name()="a:thing"]

xml = %Q{
    <?xml version="1.0" encoding="UTF-8"?>
    <a:root>
      <a:thing>stuff0</a:thing>
      <a:thing>stuff1</a:thing>
    </a:root>
}
doc = Nokogiri::XML(xml)
things = doc.xpath('//*[name()="a:thing"]')
puts things
#=> <a:thing>stuff0</a:thing>
#=> <a:thing>stuff1</a:thing>
Run Code Online (Sandbox Code Playgroud)

解决方案2-修改XML文档以定义名称空间

另一种解决方案是修改您获得的XML文件以正确定义名称空间。然后,文档将按预期使用命名空间:

xml = %Q{
    <?xml version="1.0" encoding="UTF-8"?>
    <a:root>
      <a:thing>stuff0</a:thing>
      <a:thing>stuff1</a:thing>
    </a:root>
}
xml.gsub!('<a:root>', '<a:root xmlns:a="foo">')
doc = Nokogiri::XML(xml)
things = doc.xpath('//a:thing')
puts things
#=> <a:thing>stuff0</a:thing>
#=> <a:thing>stuff1</a:thing>
Run Code Online (Sandbox Code Playgroud)