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 はもうlibxml21 つの ruby​​ XML パーサーですが、バックエンドとして は使用しません。のこぎりスピードワイズとはかなり相性がいい

また、正当な 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 に答える