我有多个XML(如下所示),其中显示了一个可选标记。此标记位于命名空间mynamespace
中
xml = %{<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:mynamespace="http://example.com/ns/1.0">
<channel>
<item>
<title>bar</title>
<mynamespace:custom_tag>some text</mynamespace:custom_tag>
</item>
<item>
<title>foo</title>
</item>
</channel>
</rss>}
Nokogiri::XML::Reader(xml).each do |node|
next if node.name!='item' || node.node_type != Nokogiri::XML::Reader::TYPE_ELEMENT
node = Nokogiri::XML.parse(node.outer_xml)
puts "-> node"
puts node.namespaces
puts node.xpath("//mynamespace:custom_tag").text
end
当Nokogiri::XML::Reader(xml)
在每个<item>
上迭代时,第一次运行输出some text
。但是,当解析第二个项时,它会抛出一个错误,该项不包含具有mynamespace
命名空间的元素。
输出为:
-> node
{"xmlns:mynamespace"=>"http://example.com/ns/1.0"}
some text
-> node
{}
Nokogiri::XML::XPath::SyntaxError: Undefined namespace prefix: //mynamespace:custom_tag
- 为什么Nokogiri在第一项中包含名称空间,而在第二项中不包含?只是因为第一个使用了名称空间,而第二个没有
- 即使当前节点中没有出现此名称空间,搜索具有名称空间的标记的解决方法是什么
- 为什么Nokogiri在第一项中包含名称空间,而在第二项中不包含?只是因为第一个使用了名称空间,而第二个没有
要了解差异,请查看第一个<item>
:的node.outer_xml
返回的内容
<item xmlns:mynamespace="http://example.com/ns/1.0">
<title>bar</title>
<mynamespace:custom_tag>some text</mynamespace:custom_tag>
</item>
与第二种:
<item>
<title>foo</title>
</item>
您会注意到,在第一种情况下,outer_xml
与输入XML:Nokogiri在父元素上包含了任何子元素的命名空间声明,这很有帮助。在第二种情况下,没有任何元素具有任何名称空间,因此Nokogiri不包含任何名称空间声明。
- 即使当前节点中没有出现此名称空间,搜索具有名称空间的标记的解决方法是什么
一个简单的解决方案是使用条件跳过不包括名称空间的元素:
Nokogiri::XML::Reader(xml).each do |node|
next unless node.name == 'item' && node.node_type == Nokogiri::XML::Reader::TYPE_ELEMENT
item_doc = Nokogiri::XML.parse(node.outer_xml)
puts "-> node"
unless item_doc.namespaces.key?("xmlns:mynamespace")
puts "Does not include namespace; skipping"
next
end
puts item_doc.xpath("//mynamespace:custom_tag").text
end
# => -> node
# some text
# -> node
# Element doesn't include namespace; skipping
您会注意到,我还用item_doc
更改了块内的变量名node
,因为Nokogiri::XML.parse
返回的是Nokogiri::XML::Document,而不是Node,而且命名非常混乱。
一个更简单的解决方案是使用Nokogiri的内存解析器,而不是XML::Reader:
doc = Nokogiri::XML(xml)
doc.xpath("//rss/channel/item/mynamespace:custom_tag").each do |node|
puts node.text
end
# => some_text
您可能使用XML::Reader,因为XML文档很大,但除非您遇到实际的内存或性能问题,否则我建议您改用这种方法。