3

我有一个相当大的 XML 文档,我想从中提取一些信息。它太大而无法保存在内存中,所以我认为 SAX 解析器是合适的。

不幸的是,生成 XML 文档的人没有仔细阅读规范,因此它包含一些非法的 XML 实体(如)。不过,除此之外,据我所知,这很好。

对于任何依赖于 libxml 的库,此类错误将禁用未来的 SAX 处理,除非它们在恢复模式下运行

/*
 * [ WFC: Legal Character ]
 * Characters referred to using character references must match the
 * production for Char. 
 */
if (IS_CHAR(val)) {
    return(val);
} else {
    ctxt->errNo = XML_ERR_INVALID_CHAR;
    if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
        ctxt->sax->error(ctxt->userData,
                         "xmlParseCharRef: invalid xmlChar value %d\n",
                         val);
    ctxt->wellFormed = 0;
    if (ctxt->recovery == 0) ctxt->disableSAX = 1;
}
return(0);

然而,两者LibXML::XML::SaxParser似乎都被Nokogiri::XML::SAX硬编码为不能在恢复模式下运行,所以一旦我遇到一个非法实体,解析就会停止(前者抛出错误,而后者只是停止显示元素开始/结束)。

有没有办法可以在恢复模式下运行其中一个(或另一个 SAX 解析器)?

4

2 回答 2

1

好吧,我找到了一个我不喜欢的解决方案。使用,我可以在运行时为被包装的值Inline::C创建一个访问器。recoveryxmlParserCtxtNokogiri::XML::SAX::ParserContext

unless LIBXML2_HEADERS and File.directory? LIBXML2_HEADERS
  STDERR.puts "Unable to locate libxml2 headers, try setting them manually using the LIBXML2_HEADERS environment variable."
  exit -1
end
class Nokogiri::XML::SAX::ParserContext
  inline do |builder|
    builder.add_compile_flags("-I" + LIBXML2_HEADERS)
    builder.include "<libxml/parser.h>"
    builder.struct_name = 'xmlParserCtxt'
    builder.accessor :recovery, 'int'
  end
end

# ...

Nokogiri::XML::SAX::Parser.new(NXSDoc.new).parse( File.open("content.rdf.u8", "r:UTF-8") ) do |ctxt|
  ctxt.recovery = 1 # turn recovery mode on
end
于 2012-10-13T12:01:09.183 回答
0

Ox是另一个 ruby​​ XML 解析器,但它不用libxml2作后端。 它与 Nokogiri speedwise 相比相当不错

而且它对合法的 XML 实体毫不关心,因此在恢复模式下运行不是问题。

修改 SAX 示例:

require 'stringio'
require 'ox'

class Sample < ::Ox::Sax
  def start_element(name); puts "start: #{name}";        end
  def end_element(name);   puts "end: #{name}";          end
  def attr(name, value);   puts "  #{name} => #{value}"; end
  def text(value);         puts "text #{value}";         end
end

io = StringIO.new(%{
<top name="sample">
  <middle name="second">
    <bottom name="third">&#8;</bottom>
  </middle>
</top>
})

handler = Sample.new()
Ox.sax_parse(handler, io)
# outputs
# start: top
#   name => sample
# start: middle
#   name => second
# start: bottom
#   name => third
# text &#8;
# end: bottom
# end: middle
# end: top
于 2012-11-09T01:24:43.987 回答