REXML::StreamParser の使い方

REXML Parser について簡単に調べてみます。
lib/rss/rexmlparser.rb にて REXML は下記のように使われています。

        REXML::Document.parse_stream(@rss, @listener)

これはストリーム型と呼ばれるパース方法で、パース後に tree が作られるのではなく、逐次 Listener へ解析結果が通知されるような形となります(SAX 型もたぶん一緒)。
よって調べるべきは Listener の方で、Listener では REXML::StreamListener を include していますので、これを調べれば使い方はある程度わかりそうです。
ということで下記サイトで調べてみました(須藤功平さんに感謝)。
http://pub.cozmixng.org/~kou/rexml-doc-ja/
目立つところでは REXML::StreamListener クラスは tag_start や tag_end といった通知系メソッドが定義されています。これらはサイトに記載がありますように、前者は要素(タグ)が現れたときに呼ばれ、後者は終了要素(タグ)が現れると呼ばれます。これがストリーム方式と言われている部分です。
# それ以外にもメソッドがありますが、何となくどんなことをしているのか判別できればよいためここではこれ以上深入りはしません。
では、試しに使ってみたいと思います。ネタは例によってこのサイトの RSS データにします。Listener を作って、それを REXML::Document.parse_stream に渡すだけです。tag_start, tag_end をオーバーライドして rss, channel, item 要素だった場合に Print するようにしてみました。

#!ruby

require 'rexml/document'
require 'rexml/streamlistener'
require 'rss'

url = "http://d.hatena.ne.jp/bazz/rss2"
rss_cont = open(url) { |u| u.read }

class RSSListener
  include REXML::StreamListener
  def tag_start(tag, attrs)
    case tag
    when "rss"
      s = ""
    when "channel"
      s = "  "
    when "item"
      s = "    "
    else
      return
    end
    print s + tag + "\n"
  end

  def tag_end(tag)
    case tag
    when "rss"
      s = ""
    when "channel"
      s = "  "
    when "item"
      s = "    "
    else
      return
    end
    print s + "/" + tag + "\n"
  end
end

listener = RSSListener.new
REXML::Document.parse_stream(rss_cont, listener)

結果は以下のようになります。

rss
  channel
    item
    /item
    item
    /item
    item
    /item
    item
    /item
    item
    /item
    item
    /item
    item
    /item
    item
    /item
  /channel
/rss

私でも、ものの数分でできてしまいました。Ruby 恐るべし。。。
では、REXML::StreamParser の使い方がわかったところで今日はおしまいにします。
RSS Lib でも、REXML::StreamParser を継承することで、rss 要素が始まったときや、channel 要素が始まったときなどに何らかの処理をすることで RSS 解析結果を蓄積していると考えられます。

参考サイト

REXML API ドキュメントの日本語版
http://pub.cozmixng.org/~kou/rexml-doc-ja/