Commons Digester

Updated: 2003-03-24 07:16:25 - [ Home | Wiki Home | Index | Changes ]

DigesterでXMLを読もう

目次

Digesterとは?
3つのステップ
パターンとルール
パターン − どんな要素が出現したら
ルール − どのような処理を行うか
スタック − Digesterはオブジェクトのスタックを一つ持っている
全ルール
生成
関連付け
プロパティ
メソッド
ファクトリクラスを使ったオブジェクトの生成
オリジナルのルールを作成する

Digesterとは?

Jakarta Commonsプロジェクトの一つとして開発されているDigesterは、XMLデータからJavaオブジェクトの生成とセットアップを行うためのライブラリです。SAXやDOMを使って自前でハンドリングするよりも、ずっと簡単にXMLファイルの読み込みが実現できます。

JAXB、Castor、Relaxerといったデータバインディングのツールと比べると、非常にシンプルである点が特徴です。スキーマからクラスを自動生成したり、オブジェクトからXMLを出力したりすることはできませんが、そのぶん、手軽にロードの処理だけを行うことができます。

3つのステップ

Digesterを使ってXMLをロードするのに必要なことは、基本的に3つだけです。

  1. Digesterのインスタンスを作成する。
  2. Digesterインスタンスに「パターン」と「ルール」のペアをセットする。
  3. Digesterインスタンスのparse()メソッドを呼んで、戻り値のObjectを取得する。

これだけです。

1と3には、あまり面白いところはありません。1は、ごく普通にnewでインスタンスを作成するだけです。3のparseメソッドは、XMLの入力(InputStreamや、File、Readerなど)を引数にして呼び出します。parseメソッドの戻り値が、ほしかったオブジェクトになっています。

ポイントは、2の「パターンとルール」です。具体的なサンプルを見る前に、「パターンとルール」、それから最も重要な「スタック」について学んでおきましょう。

パターンとルール

XMLを自由にロードするには、「XMLにどんなことが書かれていたら」「何をするか」を指定する必要があります。例えば、「textという要素に、colorという属性があったら」「TextオブジェクトのsetColor()メソッドを呼びなさい」という条件と動作の指定が必要です。

Digesterは、この「XMLにどんなことが書かれていたら」「何をするか」を表現するのに、パターンとルールという考え方を使います。「どんなことが書かれていたら」の部分を表現するのが、パターンです。「何をするか」を指定するのは、ルールです。

XML要素がパターンにマッチ → ルールでJavaオブジェクトに対する処理を実行

パターン − どんな要素が出現したら

パターンは、どんな要素が出現したらルールを起動するかを、文字列で表現します。例えば、

html/body/p

といった具合です。これは、予想どおり、html要素の子の、body要素の子の、p要素を表現しています。ワイルドカードも使えます。例えば、

*/p/a

といった具合です。

ルール − どのような処理を行うか

XML要素がパターンにマッチすると、対になるルールが起動します。ルールは、抽象クラスRuleのサブクラスです。Ruleには、

という4つのメソッドがあり、パースの各段階でそれぞれのメソッドが呼ばれます。

Ruleの実装クラスによって処理の内容を決めていくわけですが、ほとんどの場合、あらかじめ提供されたクラスを使用すれば事が足ります。たとえば、CallMethodRule(メソッドを呼び出すルール)や、SetPropertiesRule (プロパティをセットするルール)などが準備されています。

ここで、少し不安になった方もいるかもしれません。いったい「メソッドを呼び出すルール」というのは、どのオブジェクトのメソッドを対象にしているのでしょうか。「プロパティ」というのは、どのオブジェクトのプロパティのことなのでしょう。さらに言えば、オブジェクト同士を関連付けるには、どういった複雑なルールが必要になるのでしょうか。

Digesterは、オブジェクトのスタックを持つことで、とてもスマートにこの問題を解決しています。

スタック − Digesterはオブジェクトのスタックを一つ持っている

Digesterインスタンスは、内部に一つスタックを持っています。このスタックを使って、生成したオブジェクトを保持したり、ルールの対象となるオブジェクトを決めたり、オブジェクト同士の親子関係を設定したりします。

では、そろそろ実際のコードを見ていきましょう。

import org.apache.commons.digester.*;

public class DigesterSample1 {
    public static void main(String[] args) throws Exception {
        // まずはdigesterインスタンスを作成します。
        Digester digester = new Digester();

	// 次にパターンとルールのペアをセットします。
	
	// ObjectCreateRuleは、指定したクラスのオブジェクトを生成してスタックに積みます。
	digester.addRule("html/body", 
	                 new ObjectCreateRule(Body.class));
	digester.addRule("html/body/p", 
                         new ObjectCreateRule(Paragraph.class));
	
	// SetPropertiesRuleは、スタックの一番上にあるオブジェクトのsetterを呼びます。
	// 以下のXMLの場合、setId()メソッドが呼ばれます。
	digester.addRule("html/body/p",
                         new SetPropertiesRule());
	
	// SetNextRuleは、スタックの上から二番目のオブジェクトのメソッドを、
	// スタックの一番上にあるメソッドを引数にして呼び出します。
	// この時スタックは上から順にparagraph, bodyと積まれているので
	// body.setParagraph(paragraph) が実行されます。
	digester.addRule("html/body/p", 
                         new SetNextRule("setParagraph"));
	
	// test.xmlファイルをパースして、Bodyオブジェクトを取得します。
	Body body = (Body)digester.parse("test.xml");

	System.out.println(body);
    }
}

以下のtext.xmlを、このクラスで処理してみます。

<?xml version="1.0" encoding="EUC-JP"?>
<html>
<body>
<p id="text1">いろはに</p>
<p id="text2">ほへと</p>
</body>
</html>

パース中に、スタックは以下のように変化します。

  1. body開始タグに出会うと、html/bodyのパターンにマッチするので、Body.classのインスタンスが作られて、スタックにpushされます。
  2. 次にp開始タグを見つけます。同様にParagraph.classのインスタンスが作られて、スタックにpushされます。
  3. 続いて、SetPropertiesRuleが起動します。スタックの一番上にある、ParagraphオブジェクトのsetIdメソッドが呼び出されます。
  4. p終了タグでSetNextRuleが起動します。スタックの二番目の位置にあるbodyのsetParagraphメソッドが、一番上のparagraphを引数にして呼び出されます。body.setParagraph(paragraph)が実行されます。
  5. これで、最初のp要素の処理は終わりです。スタック先頭のparagraphオブジェクトがスタックからpopされます(CreateObjectRuleのend()メソッドがpopする責任をもちます)
  6. 2番目のp要素も同様に、生成、スタックにpush、プロパティの設定、body.setParagraph()呼び出し、スタックからpopという処理をたどります。
  7. 最後にbodyの終了タグに出会って、bodyオブジェクトがpopされます。
  8. 最後にpopされたオブジェクト、この場合bodyが、parseの戻り値になります。

「パターン」と「ルール」、それから「スタック」によって、わかりやすさと柔軟性が自然に両立していることがわかります。

ルール

ルールは、大きく分けて4種類あります。

バージョン1.3で提供されている全ルールは以下のとおりです。

生成

関連付け

プロパティ

メソッド

ファクトリクラスを使ったオブジェクトの生成

オリジナルのルールを作成する

まだ途中……


KOSEKI Kengo <kengo@tt.rim.or.jp>