(2007.1.14 全体的に更新。)
Rubyの標準添付XMLライブラリ、REXMLの使い方。
Warning.
(2019.1)
REXML は, 標準添付ですが, 品質・速度の両面であまりよくありません。Nokogiri (鋸) がメジャーで、長らく標準の地位で使われています。REXML の3倍速い。
最近は, 大鋸 (おが) の人気が高い。スレッドセーフ、高速、機能性もある。Yorick Peterse / oga · GitLab
REXMLの特徴
REXMLは、
- XML 1.0に準拠した、高速なXMLパーサで、DOMツリーを生成したり、XML文書を読みながらコールバックさせる (SAX2ストリームパーサ) ことができる。
- XPathの実装を含み、検索式によってDOMツリーの好きな要素を取り出すことができる。
Ruby 1.8.5にはREXML 3.1.4が添付されている。チュートリアル、APIリファレンスは下記で見られる。が、APIリファレンスのほうは不十分かつ誤りもある。使いかたをまとめてみた。
DOMツリーの生成と要素の取り出し
何はともあれ、XML文書を読み込み、パースしないと始まらない。
まずはDOMツリーを生成してみる。REXMLのパーサは、整形式 (well-formed) なXML文書を(妥当性検査せずに)パースし、DOMツリーを生成する。
Ruby
- require "rexml/document"
-
- source = <<EOF
- <?xml version="1.0"?>
- <foo><bar />
- <baz>ho<ge</baz>
- </foo>
- EOF
-
- doc = REXML::Document.new source
- p doc.root, doc.root.class
-
-
-
- p doc.elements
-
-
- p doc.elements['/foo/baz'].text
-
-
-
- p doc.elements['/foo/baz'].get_text
- p doc.elements['/foo/baz'].get_text.class
REXML::Document.newは、文字列またはIOオブジェクトを引数に取り、パースしてDOMツリーを生成する。読み込んだデータが整形式でないときは、REXML::ParseException 例外が投げられる。
Documentオブジェクトは、eachメソッドでXML宣言、要素などを順に取得できる。each メソッドでは直接の子要素がブロックに渡される。
Ruby
- require "rexml/document"
-
- doc = nil
- File.open("xmlsample.xml") {|fp|
- doc = REXML::Document.new fp
- }
-
- i = 0
- doc.each {|e|
- s = e.to_s.strip.split("\n")[0]
- print "#{i}: #{s} #<#{e.class}>\n"
- i += 1
- }
XML文書を作る
(2007.11.30 この節追加。)
今度は、何もないところからXML文書を作ってみます。次のスクリプトはATOMフィードのひな形を作ります。
単にDocument.new を呼び出すと、空のXML文書になります。
<<オペレータ、add_element(), add_text() で、外側の要素から順に作っていきます。
Ruby
- require 'rexml/document'
-
- doc = REXML::Document.new
- doc << REXML::XMLDecl.new('1.0', 'UTF-8')
-
-
- feed = doc.add_element("feed", {"xmlns" => "http://www.w3.org/2005/Atom"})
- feed.add_element("title", {'type' => 'text'}).add_text "Netsphere Feed"
-
- doc.write STDOUT
次のXML文書が出力されます。(手で整形しています。実際の出力では要素の間に空白、改行はありません。)
HTML/XML
- <?xml version='1.0' encoding='UTF-8'?>
- <feed xmlns='http://www.w3.org/2005/Atom'>
- <title type='text'>Netsphere Feed</title>
- </feed>
XPathで要素を取り出す
REXMLは、[]メソッドにXPath式を指定して、XML文書から柔軟に要素を取り出せます。
TODO:
主なクラス
(2007.12.4 更新)
REXMLでは,次の表にある左の項目は,右のクラスのオブジェクトとなります。
項目 | クラス |
XML宣言 | REXML::XMLDecl |
文書型宣言 | REXML::DocType |
処理命令 | REXML::Instruction |
要素 | REXML::Element |
コメント | REXML::Comment |
CDATAセクション | REXML::CData |
文字データ,参照 | REXML::Text |
Elementクラス (以下、本文ではREXML::は省略します。) はParentから、また、ParentとTextはChildクラスから派生しています。各クラスの関係は次のクラス図のとおりです。
DocumentクラスはElementクラスから派生しています。XML文書はただ一つのroot要素を持ちますが、だからと言ってElementから派生させるのはどうかと思わなくもありません (aDocument is_a Element, Parent or Child ではない)。
Note.
W3C Document Object Model (DOM) Coreでは、Documentインターフェイス、Elementインターフェイスは両方ともNodeから派生している。
いろいろなパーサ
REXMLには、ストリームパーサ、pull-parserも付いている。これらのパーサは、XML文書の使い方によって使い分ける。あるいは別のプログラミング言語からの移植の場合でも適宜使い分けることになるだろう。
ストリームパーサ
ストリームパーサは、特定の要素や属性にしか用事がないときなどに使う。ストリームパーサを使うときは、パーサにリスナオブジェクトを渡して、適宜コールバックさせる。
Parsers::StreamParserクラスには、StreamListenerモジュールが対応する。StreamListenerモジュールをincludeし、コールバックさせたい項目のメソッドを定義すればいい。
Ruby
- require 'rexml/parsers/streamparser'
- require 'rexml/parsers/baseparser'
- require 'rexml/streamlistener'
-
-
-
- class MyListener
- include REXML::StreamListener
-
-
-
- def tag_start(name, attrs)
- p name, attrs
- end
-
- def tag_end x
- p "tag_end: #{x}"
- end
-
-
- def xmldecl(version, encoding, standalone)
- p version, encoding, standalone
- end
- end
-
- source = File.read "01rep.xml"
- listener = MyListener.new
- REXML::Parsers::StreamParser.new(source, listener).parse
Pullパーサ
今度は、ノードを一つずつ取り出すパーサ。APIドキュメントの例は誤っている。
Parsers::PullParserオブジェクトは、has_next?メソッドとpullメソッドを持つ。pull() の戻り値はParsers::PullEventインスタンス。
Ruby
- require "rexml/parsers/pullparser"
-
- source = File.read "01rep.xml"
- parser = REXML::Parsers::PullParser.new(source)
-
- while parser.has_next?
- res = parser.pull
- if res.event_type == :start_element
- res[0..-1].each_with_index {|e, i|
- puts "#{i}: #{e} / #{e.class}"
- }
- end
- end
SAX2リスナ
Parsers::StreamParserと似たパーサとして、SAX2パーサがある。
Ruby
- require "rexml/parsers/sax2parser"
- require "rexml/sax2listener"
-
- class MyEventListener
- def receive event
- p event
- end
- end
-
-
- class MySAXListener
- include REXML::SAX2Listener
-
- def start_document
- puts "start."
- end
-
- def xmldecl version, encoding, standalone
- p version
- end
- end
-
- source = File.read "01rep.xml"
- parser = REXML::Parsers::SAX2Parser.new(source)
-
-
- parser.add_listener(MyEventListener.new)
-
-
- parser.listen(MySAXListener.new)
- parser.parse
XML文書の妥当性検査
XML文書の妥当性検査のための仕様 DTD, W3C XML Schema, RELAX NG のうち、REXMLでサポートされている。
サイト内リンク
(2007.12.6 ページを分けました)