如何让 Nokogiri 的 SAX 解析器不那么严格?

  • 本文关键字:Nokogiri SAX ruby nokogiri
  • 更新时间 :
  • 英文 :


我正在处理非常大的XML文件,所以我需要使用SAX/evented XML解析器。Nokogiri::XML::SAX 似乎是一个显而易见的选择,但是,SAX 解析器似乎会因小错误而阻塞,即使是常规 XML 解析器可以轻松恢复的错误。

在下面的示例中,<property>url 属性有一个真正应该转义到 &amp;&。Nokogiri::XML仍然能够解析<property>中的元素,但Nokogiri::XML::SAX似乎放弃了,并且从未触发<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

但是,我得到:

EntityRef: expecting ';'
0
1

有没有办法让Nokogiri忽略这些小错误?对于 Ruby 中的 SAX/push/pull/evented XML 解析,是否有更好的选择可以忽略此类错误?

改用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 解析器的全部目的。所以,不接受我自己的答案。

SAX 解析器的行为略有不同,您实际上可以将其设置为从任何错误中恢复。还可以使用错误处理程序方法来处理特定错误。

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

相关内容

  • 没有找到相关文章

最新更新