我正在处理非常大的XML文件,所以我需要使用SAX / evented XML解析器。 Nokogiri :: XML :: SAX似乎是一个明显的选择,但是,SAX解析器似乎扼杀了小错误,甚至常规XML解析器的错误都可以从中恢复。
在下面的示例中,url
的{{1}}属性有一个<property>
,应该真正转义为&
。 Nokogiri :: XML仍然能够解析&
中的元素,但Nokogiri :: XML :: SAX似乎放弃了,并且永远不会触发<property>
中元素的事件。
<property>
上面的脚本应输出:
require 'nokogiri'
class Doc < Nokogiri::XML::SAX::Document
include Enumerable
def initialize(xml)
@xml = xml
end
def each(&block)
@on_record = block
parse(@xml)
end
def parse(xml)
parser = Nokogiri::XML::SAX::Parser.new(self)
parser.parse(xml)
end
def end_element(name)
@on_record.call(name) if name == "details"
end
def error(str)
puts str
end
end
xml = <<XML
<?xml version="1.0" encoding="UTF-8"?>
<streeteasy version="1.5">
<properties>
<property url="http://example.com/?foo=bar&yin=yang">
<location>Somewhere</location>
<details>Information goes here</details>
</property>
</properties>
</streeteasy>
XML
puts Doc.new(xml).count # => 0, but should be 1
puts Nokogiri::XML(xml).xpath("//details").count # => 1
但是,我得到了:
1
1
有没有办法让Nokogiri忽略这些小错误?在Ruby中是否有更好的SAX / push / pull / evented XML解析选项可以忽略这些错误?
答案 0 :(得分:1)
使用Nokogiri的HTML SAX解析器。
更改此行
parser = Nokogiri::XML::SAX::Parser.new(self)
到这一行
parser = Nokogiri::HTML::SAX::Parser.new(self)
HTML解析器显然在恢复模式下运行libxml,并且能够从错误中恢复。这允许该示例输出所需的1/1,尽管有一些关于非标准“html”标签的抱怨。
Tag streeteasy invalid
Tag properties invalid
htmlParseEntityRef: expecting ';'
Tag property invalid
Tag location invalid
Tag details invalid
1
1
<强>更新强>
事实证明这适用于我的设计示例,但只要Nokogiri::HTML::SAX::Parser#parse
传递IO
而不是String
,它就像XML版本一样扼杀错误。我无法将文件加载到内存中......这违背了使用SAX解析器的全部目的。所以,不要接受我自己的答案。
答案 1 :(得分:0)
SAX Parser的行为略有不同,您实际上可以将其设置为从任何错误中恢复。您还可以使用错误处理程序方法来处理特定错误。
class MyDoc < Nokogiri::XML::SAX::Document
def error(error)
puts "An error occurred: #{error}"
end
def start_element(name, attributes = [])
puts "found a #{name}"
end
end
parser = Nokogiri::HTML::SAX::Parser.new(MyDoc.new)
parser.parse(open(url)) do |ctx|
ctx.recovery = true
end