連載 役に立つXMLツール集(3)
Castorでデータバインディングに挑戦しよう

XMLプログラミングでは、DOMやSAXといったAPIを使用すると単調なコードを繰り返し書くことになり生産性が上がらないものだ。本連載では開発者が“楽をする”ために役立つXML関連ツールを紹介していく。(編集局)

www.netpotlet.com
原田洋子
2004/1/8

Castorの概要

主な内容
Castorの概要
Castorの入手とセットアップ
開発手順
スキーマの作成
スキーマのコンパイル
読み込みアプリケーションの作成
読み込みアプリケーションの実行
文書操作アプリケーションの作成
文書操作アプリケーションの実行
まとめ&サンプル・ダウンロード

 前回のJAXBに引き続き、XMLモデルからオブジェクトモデルへのマッピングを行う、データバインディングツールを紹介します。今回、取り上げるのはCastorです。

 CastorはExolab Groupが開発したオープンソースのデータバインディングツールで、無償で利用できます。この分野では最もよく使われているツールといえるでしょう。また、CastorはXML−オブジェクト間のマッピングだけではなく、SQLやLDAPとオブジェクトのマッピングにも対応しています。今回の記事ではSQL/LADPは取り上げませんが、このようなところまでカバーしている多機能な点も人気の一因のようです。

 現在、Castorが対象にしているスキーマ言語はW3C XML Schemaのみです。すでにあるDTDの定義についてはW3C XML Schemaに変換するコンバータが付属していますので、Castorでも利用できます。ただし、洗練されたツールではないようで、変換できないDTDが多々ありました。変換できない場合は前回、紹介したTrangを利用するといいでしょう。Trangなら、どのようなDTDやRELAX NGからでもW3C XML Schemaによる定義を生成できるので、スキーマ言語による制約から解放されます。また、W3C XML SchemaやRELAX NGなどのスキーマ言語による構造を定義していなくても、CastorはXMLインスタンスやJavaクラス定義からほかのモデルへのマッピングも行えるようになっています。

 このように多機能で広く使われているCastorですが、独自APIが採用されているのみで、データバインディングのためのAPIであるJAXB仕様には対応していません。現時点ではJAXB APIへの対応について特にアナウンスはないので、当面、Castor独自APIのまま開発が進められるものと思われます。

 これから紹介するサンプルはJAXBとの違いを理解しやすくするため、前回と同じものをCastor用に修正して説明していきます。本記事のプログラムは

  • J2SDK 1.4.1_06
  • Castor 0.9.5.1
  • Eclipse 2.1.2/2.1.x Translations

を使用し、Linux上で動作を確認しています。


Castorの入手とセットアップ

 Castorは次のURLからダウンロードできます。

  http://www.castor.org/download.html

 2003年12月時点で最新版は0.9.5.2ですが、このバージョンはW3C XML SchemaからJavaのソースコードを生成するSourceGeneratorがうまく動かなかったため、本記事では1つ前のバージョンである0.9.5.1をftp://ftp.exolab.org/pub/castor/から取得して使いました。

 必要なアーカイブは

  • ドキュメントを含むすべてが同梱されているcastor-0.9.5.1.tgz(zip)
  • ドキュメントを除く、すべての機能を含むcastor-0.9.5.1.jar
  • XML、オブジェクト間のマッピング機能のみのcastor-0.9.5.1-xml.jar

のどれか1つです。tgzあるいはzip形式を取得した場合は適当なディレクトリに展開しておきます。

Eclipseのセットアップ

本記事はEclipse上で開発/実行することを前提にしますので、クラスパスのセットアップなどはEclipse上で行います

※注
Eclipseの詳細には触れませんので、@ITのEclipse関連記事などを参照してください。また、前回の記事に掲載している操作画面も参考にしてください。


1. Javaプロジェクト作成(例:streamプロジェクト)
 ファイル → 新規 → プロジェクト → Java(左ペイン) →
 Java プロジェクト(右ペイン) → 次へ → プロジェクト名(stream) → 終了

2. Javaのビルド・パス設定
 パッケージ・エクスプローラーでプロジェクト(stream)選択 →
 右クリック → プロパティー → Javaのビルド・パス(左ペイン) →
 ライブラリー選択 → 外部 JAR の追加 →
 castor-0.9.5.1-xml.jar(あるいは castor-0.9.5.1.jar)
 アーカイブを追加する(画面

3. フォルダ作成
 ソース・フォルダ
 パッケージ・エクスプローラーでプロジェクト(stream)選択 →
 右クリック → 新規 → ソース・フォルダー → フォルダー名(src) → 終了

 スキーマ用フォルダ
 パッケージ・エクスプローラーでプロジェクト(stream)選択 →
 右クリック → 新規 → フォルダー → フォルダー名(schemas) → 終了

 XML文書用フォルダ
 パッケージ・エクスプローラーでプロジェクト(stream)選択 →
 右クリック → 新規 → フォルダー → フォルダー名(docs) → 終了


開発手順

 CastorはXMLインスタンスやJavaオブジェクトからもマッピングを始められますが、やはり構造を定義したスキーマから始めるのが基本ですから、図1に示す手順で開発を進めます。これは前回のJAXBによる開発方法を説明した図1とまったく同じです。詳細は前回の説明を参照してください。

 1 データバインディングツールを利用した開発手順


スキーマの作成

 最初に文書構造をスキーマ言語を使って定義します。冒頭で触れたようにJAXBのサンプルと同じものを使いますから、スキーマも前回記事のリスト3のkeyboard.xsdを利用します。ただし、このままではCastorが解釈できない定義があるので、その部分を修正しました。

 本来は

    <key size="large">
      <label type="control">space</label>
    </key>

    <key>
      <size>large</size>
      <label><type>control</type>space</label>
    </key>

のように属性でも、要素でも同じ名前でtypeやsizeを指定できるようにしたいのですが、そのような定義を正しく解釈してJavaのコードを生成できるデータバインディングツールはあまり多くありません。次回に紹介する予定のRelaxerはできるのですが、残念ながらCastorにはできません。

 Castorはこの問題を回避するために、バインディングファイル(通常、binding.xml)により、外部から詳細を指示する方法が用意されています。バインディングファイルでは同名の要素名、属性名の解決のほか、要素や属性をデフォルト以外のJavaクラス/インターフェイスへマッピングしたい場合にも利用します。本記事ではスキーマを修正しても問題にはならないので、Castorがコードを生成できるように、次の2種類のルールでXML文書を記述することにしました。

    <key attrSize="large">
      <label attrType="control">space</label>
    </key>

    <key>
      <size>large</size>
      <label><type>control</type>space</label>
    </key>

 このサンプルで利用するXML文書はリスト1のkeyboard1.xml、リスト2のkeyboard2.xmlの2種類、スキーマ定義がリスト3のkeyboard.xsdです。

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <keyboard>
 3   <key>
 4     <label attrType="number"
 5            default="true">
 6       1
 7     </label>
 8     <label attrType="character">!</label>
 9   </key>
10   <key>
11     <label attrType="character">a</label>
12   </key>
13   <key attrSize="medium">
14     <label attrType="control">shift</label></key>
15   <key>
16     <label default="true">
17       <type>number</type>
18       2
19     </label>
20     <label>
21       <type>character</type>
22       @
23     </label>
24   </key>
25   <key>
26     <size>large</size>
27     <label><type>control</type>space</label>
28   </key>
29 </keyboard>
リスト1 keyboard1.xml

 1 <?xml version="1.0" encoding="EUC-JP"?>
 2 <keyboard>
 3   <key>
 4     <label attrType="number"
 5            default="true">
 6       1
 7     </label>
 8     <label attrType="character">あ</label>
 9     <label attrType="character">い</label>
10   </key>
11   <key>
12     <label attrType="number"
13            default="true">
14       2
15     </label>
16     <label attrType="character">か</label>
17     <label attrType="character">A</label>
18   </key>
19   <key>
20     <label attrType="character"
21            default="true">
22       #
23     </label>
24     <label attrType="control">記号</label>
25   </key>
26 </keyboard>
リスト2 keyboard2.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
              elementFormDefault="qualified">
 3   <xs:element name="keyboard">
 4     <xs:complexType>
 5       <xs:sequence>
 6         <xs:element minOccurs="0"
                       maxOccurs="unbounded" ref="key"/>
 7       </xs:sequence>
 8     </xs:complexType>
 9   </xs:element>
10   <xs:element name="key">
11     <xs:complexType>
12       <xs:sequence>
13         <xs:group minOccurs="0" ref="size"/>
14         <xs:element maxOccurs="unbounded" ref="label"/>
15       </xs:sequence>
16       <xs:attributeGroup ref="size"/>
17     </xs:complexType>
18   </xs:element>
19   <xs:group name="size">
20     <xs:sequence>
21       <xs:element minOccurs="0" ref="size"/>
22     </xs:sequence>
23   </xs:group>
24   <xs:element name="size" type="size.list"/>
25   <xs:attributeGroup name="size">
26     <xs:attribute name="attrSize" type="size.list"/>
27   </xs:attributeGroup>
28   <xs:simpleType name="size.list">
29     <xs:restriction base="xs:token">
30       <xs:enumeration value="large"/>
31       <xs:enumeration value="medium"/>
32       <xs:enumeration value="small"/>
33     </xs:restriction>
34   </xs:simpleType>
35   <xs:element name="label">
36     <xs:complexType mixed="true">
37       <xs:sequence>
38         <xs:element minOccurs="0" ref="type"/>
39       </xs:sequence>
40       <xs:attribute name="default" type="xs:boolean"/>
41       <xs:attribute name="attrType" type="type.list"/>
42     </xs:complexType>
43   </xs:element>
44   <xs:element name="type" type="type.list"/>
45   <xs:simpleType name="type.list">
46     <xs:restriction base="xs:token">
47       <xs:enumeration value="control"/>
48       <xs:enumeration value="character"/>
49       <xs:enumeration value="number"/>
50     </xs:restriction>
51   </xs:simpleType>
52 </xs:schema>
リスト3 keyboard.xsd

スキーマのコンパイル

 CastorのコンパイラはSourceGeneratorと呼ばれるツールで、同名のクラスがその機能を提供しています。実行は前回のJAXB同様、Antを利用するほか、Castorプラグインを使う方法もあります。

 Castorプラグインは次のURLから取得します。

  http://xdoclipse.sourceforge.net/

 このプラグインをEclipseに組み込んで再起動した後、拡張子がxsdのファイルを選択し、右クリックすると、図2に示すように“Generate Castor XML Sources ...”メニューが表示されます。このメニューを選択すると、図3に示す設定ウィンドウが開くので、ここでパッケージ名などを指定し、OKボタンを押すとJavaクラスが生成されます。

2 Castorプラグイン(Generate Castor XML Sources ...)



3 Castorプラグインの設定(クリックで拡大します)

 本記事ではリスト4のビルドファイルを用意し、Antを実行しました。このとき図4のディレクトリ構成になっていることを想定しています。

 SourceGenerator実行時に指定できる主な引数は表1のようになっています。ほかのオプションなど詳細はCastor付属のドキュメント「doc/sourcegen.html」、あるいは「doc/SourceGeneratorUser.pdf」にありますので参照してください。また、継承させるクラスを指定する、XML namespacesをパッケージに対応させる、といった操作はプロパティ「castorbuilder.properties」で設定できます。

オプション 引数 意味
i ファイル名 [必須]入力のW3C XML Schemaファイルを指定する
package パッケージ名 生成されるクラスのパッケージ名を指定する
dest パス 生成されるソースコードのパスを指定する
line-separator unix/mac/win 改行コードを指定する
types type-factory Collection API利用の有無を指定する
h   ヘルプ。使用方法の説明
f   致命的ではないエラーを無視する
binding-file バインディングファイル名 クラス生成の詳細指定を行うバインディングファイル名を指定する
表1 主なSourceGeneratorの引数


4 streamプロジェクトのディレクトリ構成(1)



 1 <project name="stream" default="castor" basedir=".">
 2   <property name="castor.home"
               value="/usr/local/java/xml/castor-0.9.5.1"/>
 3   <property name="schema.dir" value="schemas"/>
 4   <property name="xsdfile" value="keyboard.xsd"/>
 5   <property name="package" value="com.netpotlet.canoe"/>
 6   <property name="dest" value="./src"/>
 7
 8   <path id="classpath">
 9     <pathelement
        location="${castor.home}/castor-0.9.5.1-xml.jar"/>
10   </path>
11
12   <target name="castor">
13     <echo
        message="Generating Java classes and interfaces ..."/>
14     <java
        classname="org.exolab.castor.builder.SourceGenerator"
        fork="yes">
15       <classpath refid="classpath" />
16       <arg line="-f -i ${schema.dir}/${xsdfile} -package ${package} -dest ${dest}" />
17     </java>
18   </target>
19
20 </project>
リスト4 build.xml(赤字部分は環境に合わせて変更してください)

 ビルドファイルを作成したら、設定して実行します(図5参照)。

 実行 → 外部ツール → 外部ツール → Antビルド選択 → 新規 →
 ロケーション/基本ディレクトリ設定 → 適用 → 実行

5 Ant実行のための設定(クリックで拡大します)

 2回目以降のAnt実行は外部ツール実行ボタンをクリックするだけです。

 ビルドが成功するとソース・フォルダにリスト4のpackageプロパティ(5行目)で指定したパッケージに所属するソースコードが自動生成されます。ここで、次のようにすると、自動生成されたソースコードが表示されるとともに、コンパイルされます。

 パッケージ・エクスプローラーでプロジェクト(stream)選択 →
 右クリック → 最新表示

 本記事の場合、表2のパッケージ、クラスが自動生成されます。

パッケージ クラス
com.netpotlet.canoe Key.java
Keyboard.java
KeyboardDescriptor.java
KeyDescriptor.java
Label.java
LabelDescriptor.java
Size.java
SizeDescriptor.java
com.netpotlet.canoe.types Size_list.java
Size_listDescriptor.java
Type_list.java
Type_listDescriptor.java
表2 リスト3のkeyboard.xsdから生成されたクラス


読み込みアプリケーションの作成

 スキーマをコンパイルしてAPIが生成されたので、今度は簡単なアプリケーションを試してみます。ここでも前回のJAXBと比較しやすいように、XML文書を読み込むプログラムを同様のスタイルで作ってみます。アプリケーション作成時には自動生成されたAPIのほか、Castorが提供するAPIも使います。

 リスト5がXML文書を読み込むCastorReaderクラスです。JAXB同様、CastorでもXML文書の読み込みをアンマーシャル(Unmarshal)といい、これを行うには、自動生成されたKeyboardクラスのunmarshalメソッドを使うか、org.exolab.castor.xml.Unmarshallerクラスを使います。

 リスト5はパッケージcom.netpotlet.testに属するクラスとして定義しましたので、次のようにしてパッケージ/クラスを作ります。

 パッケージ・エクスプローラーでソース・フォルダー(src)選択 →
 右クリック → 新規 → パッケージ →
 パッケージ名(com.netpotlet.test) → 終了

 パッケージ・エクスプローラーでパッケージ(com.netpotlet.test)選択 →
 右クリック → 新規 → クラス → クラス名(CastorReader) → 終了


 1 package com.netpotlet.test;
 2
 3 import java.io.FileNotFoundException;
 4 import java.io.FileReader;
 5
 6 import org.exolab.castor.xml.MarshalException;
 7 import org.exolab.castor.xml.ValidationException;
 8
 9 import com.netpotlet.canoe.Key;
10 import com.netpotlet.canoe.Keyboard;
11 import com.netpotlet.canoe.Label;
12 import com.netpotlet.canoe.Size;
13 import com.netpotlet.canoe.types.Size_list;
14 import com.netpotlet.canoe.types.Type_list;
15
16 public class CastorReader {
17     private Keyboard keyboard;
18     
19     CastorReader(String filename)
20         throws MarshalException,
21                ValidationException,
22                FileNotFoundException {

           //XML文書を読み込む(アンマーシャル)
23         keyboard =
24             (Keyboard) Keyboard.unmarshal(
                   new FileReader(filename));
25         keyboard.validate();
26     }
27     
28     Keyboard getKeyboard() {
29         return keyboard;
30     }
31     
32     private void report() {
33         String newline = System.getProperty("line.separator");
34         String defaultSize = "small";
35

           //要素keyの配列を取り出す
36         Key[] keys = keyboard.getKey();
37         StringBuffer sb = new StringBuffer();
38         for (int i=0; i<keys.length; i++) {
39             Key key = keys[i];

               //属性か要素にセットされている
               //size(attrSize)を取得する
40             sb.append("size: ");
41             Size_list size_list = key.getAttrSize();
42             Size size = key.getSize();
43             if (size_list != null) {
44                 sb.append(size_list.toString());
45             } else if (size != null) {
46                 sb.append(size.getSize().toString());
47             } else {
48                 sb.append(defaultSize);
49             }

               //要素labelの配列を取り出す
50             Label[] labels = key.getLabel();
51             for (int j=0; j<labels.length; j++) {
52                 sb.append(" [");
53                 Label label = labels[j];

                   //この要素がdefault指定かどうかをチェックする
54                 if (label.getDefault()) {
55                     sb.append("(default)");
56                 }

                   //属性か要素にセットされている
                   //type(attrType)を取得する
57                 sb.append("type: ");
58                 Type_list type_list = label.getType();
59                 if (type_list != null) {
60                     sb.append(type_list.toString());
61                 } else {
62                     type_list = label.getAttrType();
63                     if (type_list != null)
64                         sb.append(type_list.toString());
65                 }

                   //label要素のテキストを取得する
66                 String contents = label.getContent();
67                 sb.append(", label: ");
68                 sb.append(contents);
69                 sb.append("]");
70             }
71             sb.append(newline);
72         }
73         System.out.println(new String(sb));
74     }
75
76     public static void main(String[] args) throws Exception {
77         if (args.length < 1) {
78             throw new Exception("arguments are not enough");
79         }
80         CastorReader reader =
81             new CastorReader(args[0]);
82         reader.report();
83     }
84 }
リスト5 CastorReader.java


読み込みアプリケーションの実行

 XML文書の読み込みを行うアプリケーションを実行してみましょう。このアプリケーションはXML文書を実行時の引数から取得するので、次のように設定、実行します(図6、7参照)。なお、XML文書は図4のdocsディレクトリに置いてあります。

 実行 → 実行... → Javaアプリケーション → 新規 → メインタブ →
 プロジェクト名(stream)/
 メイン・クラス(com.netpotlet.test.CastorReader) →
 引き数タブ → プログラム引き数(docs/keyboard1.xml) → 適用 → 実行

6 実行アプリケーションの設定(クリックで拡大します)



7 実行時の引き数指定(クリックで拡大します)


 XML文書が正しく書けていれば図8のように出力されます。

8 実行結果



文書操作アプリケーションの作成

 JAXBで試したのと同様に、要素や属性を追加してXML文書を出力するアプリケーションも試してみましょう。リスト6のCastorCreatorがそのためのクラスです。CastorでもJAXB同様、XML文書の出力をマーシャル(Marshal)といい、これを行うには自動生成されたKeyboardクラスのmarshalメソッドを使うか、org.exolab.castor.xml.Marshallerクラスを使います。両者の違いですが、デフォルトの設定でよい場合はKeyboardクラスのmarshalメソッドを、マーシャル時にXML文書のエンコードを指定するなどの操作を行いたい場合はMarshallerを使います。このサンプルではMarshallerを使い、出力を加工しました。

 1 package com.netpotlet.test;
 2
 3 import java.io.IOException;
 4 import java.io.OutputStreamWriter;
 5
 6 import org.exolab.castor.mapping.Mapping;
 7 import org.exolab.castor.mapping.MappingException;
 8 import org.exolab.castor.xml.Marshaller;
 9 import org.exolab.castor.xml.MarshalException;
10 import org.exolab.castor.xml.ValidationException;
11
12 import com.netpotlet.canoe.Key;
13 import com.netpotlet.canoe.Keyboard;
14 import com.netpotlet.canoe.Label;
15 import com.netpotlet.canoe.types.Type_list;
16
17 public class CastorCreator {
18     private Keyboard keyboard;
19     
20     CastorCreator(String filename)
21         throws MarshalException,
22                ValidationException,
23                IOException, MappingException {

           //元文書を取得する
24         keyboard =
25             (new CastorReader(filename)).getKeyboard();

           //元文書にいくつかの要素を追加する
26         updateDocument();

           //マーシャラーを取得する
27         Marshaller marshaller =
28             new Marshaller(new OutputStreamWriter(System.out));

           //XML宣言の encoding を euc-jp にする
29         marshaller.setEncoding("euc-jp");

           //出力を加工するためのマッピングを
           //マーシャラーにセットする
30         Mapping mapping = new Mapping();
31         mapping.loadMapping("mapping.xml");
32         marshaller.setMapping(mapping);

           //標準出力に表示する
33         marshaller.marshal(keyboard);
34     }
35     
36     private void updateDocument() {
37         String[] labels1 = {
38             "う", "え", "お"
39         };
40         String[] labels2 = {
41             "な", "に", "ぬ", "ね", "の"
42         };

           //1つ目のkey要素にlabel要素を追加する
43         Key key1 = (Key)keyboard.getKey(0);
44         updateKey(key1, labels1);

           //3つ目のkey要素としてlabel要素を含む key要素を
           //生成して追加する
45         Key key2 = createKey(labels2);
46         keyboard.addKey(2, key2);
47     }
48     
49     private void updateKey(Key key,
50                            String[] labels) {
51         for (int i=0; i<labels.length; i++) {
52             key.addLabel(getLabel(Type_list.CHARACTER,
                                     labels[i]));
53         }
54     }
55     
56     private Key createKey(String[] labels) {
57         Key key = new Key();
58         key.addLabel(getLabel(Type_list.NUMBER, "5"));
59         for (int i=0; i<labels.length; i++) {
60             key.addLabel(getLabel(Type_list.CHARACTER,
                                     labels[i]));
61         }
62         return key;
63     }
64     
65     private Label getLabel(Type_list type,
66                            String text) {
67         Label label = new Label();
68         label.setAttrType(type);
69         label.setContent(text);
70         return label;
71     }
72     
73     public static void main(String[] args) throws Exception {
74         if (args.length < 1) {
75             throw new Exception("arguments are not enough");
76         }
77         new CastorCreator(args[0]);
78     }
79 }
リスト6 CastorCreator.java


マッピングファイル

 リスト6のCastorCreatorは30〜32行目でマッピングファイルを取得し、Marshallerにセットしています。マッピングファイルは、JavaクラスをどのようにXML文書にマップするかを詳細に指定するために利用するファイルです。拡張子で分かるようにXML文書です。CastorにはオブジェクトモデルからXMLモデルへのマッピング機能があると説明しましたが、そこで使われるのがマッピングファイルです。

 このサンプルはXMLモデルからスタートしているので、マッピングファイルは必須ではありませんが、今回は出力を加工する目的で利用しました。マッピングファイルを使わない場合、ソースコード生成の元になったスキーマに従った構造でXML文書が出力されます。このサンプルはバインディングファイルを使わず、Castor用にスキーマを修正したので、属性のattrSize、attrTypeもそのまま出力されます。これではJAXBの出力と同じになりませんのでマッピング機能を利用し、最終的に同じ結果が得られるようにしてみました。

 ここで利用したマッピングファイルがリスト7のmapping.xmlです。自動生成されたクラスと比較すれば何をしているのか分かると思います。特に16〜18行目に注目してください。ここではLabelクラスのインスタンス変数attrTypeをXML文書では属性typeにマップするように指定しています。こうすると、スキーマを修正したにもかかわらず、修正前のスキーマが適用されているかのように表示できます。

 1 <mapping>
 2   <class name="com.netpotlet.canoe.Keyboard">
 3     <field name="key" type="com.netpotlet.canoe.Key">
 4       <bind-xml name="key" node="element"/>
 5     </field>
 6   </class>
 7   <class name="com.netpotlet.canoe.Key">
 8     <field name="label" type="com.netpotlet.canoe.Label">
 9       <bind-xml name="label" node="element"/>
10     </field>
11   </class>
12   <class name="com.netpotlet.canoe.Label">
13     <field name="default" type="boolean">
14       <bind-xml name="default" node="attribute"/>
15     </field>
16     <field name="attrType"
              type="com.netpotlet.canoe.types.Type_list">
17       <bind-xml name="type" node="attribute"/>
18     </field>
19     <field name="content" type="string">
20       <bind-xml node="text"/>
21     </field>
22   </class>
23 </mapping>
リスト7 mapping.xml


プロパティファイル

 Castorはデフォルトでは出力するXML文書のインデントを行いません。改行やインデントは見た目にはきれいですが、データ的には

  終了タグ、改行、スペース、スペース、スペース、スペース、開始タグ

のようになり、XML文書のサイズが大きくなるほどに、改行やインデントが占める容量を無視できなくなるためです。JAXBではインデントの制御をプログラム内で行いましたが、Castorはプロパティファイルで指定します。そのためのプロパティがリスト8のcastor.propertiesです。castor.propertiesでさまざまな詳細設定の指定が可能ですが、ひな型はソースコード一式の中のcastor-0.9.5.1/src/main/org/exolab/castor/castor.propertiesにあるので、ひととおり眺めておくといいでしょう。

# THE CASTOR PROPERTIES FILE
# This file specifies values for Castor run-time
# which may be configured by the user.
# $Id: castor.properties,v 1.3 2003/09/17 03:52:41 kvisco Exp $

# True if all documents should be indented on output by default
#
org.exolab.castor.indent=true
リスト8 castor.properties

 以上のソース、マッピングファイル、プロパティファイルを含むstreamプロジェクトは最終的に図9に示す構成になっています。

9 streamプロジェクトのディレクトリ構成(2)



文書操作アプリケーションの実行

 このアプリケーションも実行してみましょう。今度もXML文書を実行時の引数から取得するので、次のように設定、実行します。XML文書は図9のdocsディレクトリに置いてあります。

 実行 → 実行... → Javaアプリケーション → 新規 → メインタブ →
 プロジェクト名(stream)/
 メイン・クラス(com.netpotlet.test.CastorCreator) →
 引き数タブ → プログラム引き数(docs/keyboard2.xml) → 適用 → 実行

 問題なく実行されれば、図10のように出力されます。

10 実行結果

まとめ&サンプル・ダウンロード

 今回は、最も普及しているといっていいデータバインディングツールのCastorを取り上げましたが、いかがだったでしょうか。JAXBという標準仕様に対応していないものの、要素がクラスにマップされ、それを直接インスタンス化できるなど、分かりやすく使いやすいツールであることが分かったのではないでしょうか。実用的という表現が似合うツールです。長く現場で使われてきたので、デフォルトと詳細設定のアプローチなど、理にかなった方法が採り入れられているツールともいえるでしょう。

 標準APIに非対応となってしまったのは、歴史的な経緯によるところが大きいと思われます。現時点ではJAXB仕様への対応は不明ですが、JAXB実装のソースコードが公開されるようになったり、ApacheサブプロジェクトでオープンソースのJAXB API実装JaxMeが開発されたりといった動きが出てきたので、Castorもいずれ対応を考えることになると思われます。

 今回はXMLモデルとオブジェクトモデルのマッピング方法を説明しましたが、いずれJDOなどSQL関連機能を取り上げる予定です。

 最後に、Castorの日本語による解説として次の書籍を紹介します。

  • 『Javaオープンソース徹底攻略』
    岡本隆史、吉田英嗣、山口卓也、樋山大輔 著
    株式会社ソフト・リサーチ・センター ISBN4-88373-190-1

 今回使用したプログラムやファイル類は以下からダウンロードできます。

  • stream.zip(50Kbytes)
     
    Windows環境で利用される場合、euc-jpをWindows-31Jなど適当なエンコーディングに変更してください。また、日本語を含むファイルはEUC-JPになっていますので、あらかじめ文字コードを変えてから利用されるといいでしょう。

 次回はRelaxerを取り上げる予定です。お楽しみに。(次回に続く


======== my confessions ========

やってしまいました(^^;; 。前回の記事ですが、「character」を「charactor」と信じこんだまま、最後まで気付かず公開してしまいました。スミマセン。今回の記事では何事もなかったかのように「character」になっています。前回のJAXBと比較して読まれる場合は、charactor → characterと読み替えていただければ幸いです。なおプログラムの動作上、問題はありません。(苦笑)

関連記事

 ・SEのためのXML Schema入門
 ・XMLテクニック集
 ・Javaで実現するDOM/SAXプログラミング



「連載 役に立つXMLツール集」


XML & SOA フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

HTML5+UX 記事ランキング

本日月間