実際のSAXプログラミング
[1]コンテントハンドラの作成
繰り返しになりますが、DOMのプログラミングではパーサを用いてDOMのオブジェクトのツリーを取得し、それに対して処理を行いました。
SAXでは、構文解析を行う過程で発生するイベントを処理するリスナ(コンテントハンドラ)を作成する必要があります。これは、前節で挙げたContentHandlerのインターフェイスに定義されているメソッドを、あらかじめプログラマーがすべて実装するということになります。
ただし実際のところ、すべてのイベントを処理したいということは余りないでしょう。例えば、XML文書内に記述されたテキストデータだけを取り出したいならば、開始タグや終了タグに対応するイベントを処理する必要はありません。
このためSAXでは、org.xml.sax.helpers.DefaultHandlerという形で、コンテントハンドラのデフォルト実装が提供されています。これを継承することで、必要なイベント処理のみをオーバーライドして記述することができるようになります(JAXPで生成されるSAXパーサでは、そもそもDefaultHandlerを継承したコンテントハンドラしか利用できなくなっています)。
今回は、このDefaultHandlerを継承し、ContentHandlerの主なメソッドを定義したサンプルを作成してみます(リスト2)。
import org.xml.sax.*; import org.xml.sax.helpers.*; public class SampleHandler extends DefaultHandler { public void startDocument() { System.out.println("startDocument"); } public void endDocument() { System.out.println("endDocument"); } public void startElement(String namespaceURI, String localName, String qName, Attributes atts) { System.out.println("startElement: " + qName); } public void endElement(String namespaceURI, String localName, String qName) { System.out.println("endElement: " + qName); } public void characters(char[] ch, int start, int length) { System.out.print("characters: "); for (int i = 0; i < length; i++) { System.out.print(ch[start + i]); } System.out.println(); } }
各メソッドがどのようなイベントを処理するのかを以下にまとめておきました。いずれのメソッドでも、単純にイベントが発生したことのみを表示するだけの処理を記述しています。
●startDocument, endDocument
サンプルでは省略しましたが、ドキュメントの先頭およびドキュメントの最後でもイベントは発生します。そのイベントを処理するためのメソッドです。
●startElement, endElement
開始タグおよび終了タグを読み込んだ際に発生するイベントを処理するためのメソッドです。引数は、それぞれ以下の情報を表しています。
namespaceURI | ネームスペースを表すURI |
---|---|
localName | タグのローカル名 |
qName | タグの修飾名 |
Attributes | 属性情報 |
タグの名称については、XML名前空間の話が関連してくるので少し厄介です。本連載ではXML名前空間についての議論には触れていませんので、ここでは詳細は省きます。取りあえず、Xerces 1.2.3の実装では、qNameを参照すればタグの修飾名を取得することができますので、こちらを利用しておきます。
●characters
テキストデータを読み込んだ際に発生するイベントを処理するためのメソッドです。読み込んだテキストが引数で与えられます。
ch | 読み込んだテキストデータが格納された配列 |
---|---|
start | 配列内でテキストデータが配置されているオフセット |
length | 読み込んだテキストデータの長さ |
[2]パーサファクトリおよびパーサの生成
コンテントハンドラを準備しましたので、次はSAXパーサのインスタンスを生成します。
ここで、DOMのパーサに相当するドキュメントビルダを生成したときのことを少し思い出してみてください。ドキュメントビルダを生成するために、まずドキュメントビルダファクトリを生成しました。
SAXの場合も同様です。SAXパーサを生成するために、まずパーサファクトリを生成します。このパーサファクトリを用いて、SAXパーサのインスタンスを生成するのです(リスト3)。
try { SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser parser = factory.newSAXParser(); } catch (SAXException e) { } catch (ParserConfigurationException e) {}
このように2段階のステップを踏むようになっているのは、もちろん使用するSAXのライブラリによらず、統一したインターフェイスでパーサを生成するためです。
[3]構文解析処理の呼び出し
パーサを生成できたら、あとはXML文書とコンテントハンドラを引数に構文解析の処理parseを呼び出すだけです(リスト4)。
try { String url = "...."; // XML文書の実際のURL DefaultHandler handler = new SampleHandler(); parser.parse(url, handler); } catch (IOException e) { } catch (SAXException e) { }
SAXParserのparseには、幾つかのバリエーションがあります。これらは構文解析を行うXML文書をどのようにして与えるかという点、すなわち第1引数の型が異なります。
InputStreamやFileオブジェクトを引数として与えることができるようになっていますが、リスト4ではXML文書のURLを文字列で与えるものを使用しています。
リスト1のサンプル文書を、実際にこのハンドラの例で実行してみます。ここで示したコードは部分的なものなので、少し書き足す必要がありますが、ぜひ実際に書いてみてください。
参考までに、実行結果をリスト5に示しておきます。
startDocument startElement: root characters: startElement: data characters: あいうえお endElement: data characters: endElement: root endDocument
まず、最初と最後にstartDocumentとstartElementが呼ばれていることを確認してください。
あとは順にstartElement、characters、startElement…と続いていきます。この部分の呼び出しの順序は、図1、図2、図3で見たとおりですので、対応がとれているか確認してみてください。
SAXって簡単?
今回は、SAXを用いてXML文書を読むために必要な一通りの手順について見てみました。割とあっけなかったのではないでしょうか? このあたり、Simple API for XMLの名前のとおりだといえなくもありません。
ただし、今回はXML文書を最初から最後まで順に読んでみるという非常に簡単なサンプルを試してみただけです。
エレメントの階層が深く、なおかつ情報が複数の階層に分散しているような場合は、ハンドラで取り出した情報をどのように整理して格納するかというところで少しテクニックが必要になってきます。
次回はこのあたりを含めて、SAXを用いた例題を見てみることにしましょう。それでは皆さん、次回までごきげんよう!
Copyright © ITmedia, Inc. All Rights Reserved.