我有一个需要解析的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>
我正在使用Nokogiri查询文档:
doc = Nokogiri::XML(open('text.xml'))
things = doc.xpath('//a:thing')
失败并给出以下错误:
Nokogiri::XML::XPath::SyntaxError: Undefined namespace prefix: //a:thing
从我的研究中,我发现我可以在 xpath
方法中为前缀指定命名空间:
things = doc.xpath('//a:thing', a: 'nobody knows')
这将返回一个空数组。
我获取所需节点的最佳方式是什么?
问题是命名空间未在 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
#=> ""
解决方案 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>
解决方案 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>