JavaにおけるXML: データ・バインディング 第1回: コード生成方式 -- JAXBなどなど

DTDまたはスキーマからデータ・クラスを生成する

エンタープライズJavaの専門家であるDennis Sosnoski氏が、XML文書用のW3C XML SchemaまたはDTDの文法からコード生成して、XMLデータ・バインディングを行ういくつかの手法を取り上げます。まずは、ようやくJava Community Process (JCP) から近々リリースされる運びとなった JAXB規格に触れ、それから現時点で利用できる他のいくつかのフレームワークを概観し、最後に、文法からのコード生成をアプリケーションにうまく応用する方法や条件を説明する、という流れになっています。

Dennis Sosnoski, Consultant, Sosnoski Software Solutions, Inc.

Dennis Sosnoski はシアトル地域にある Java 技術のコンサルティング会社、Sosnoski Software Solutions, Inc. の創立者で、主席コンサルタントでもあり、また XML や Web サービスに関するトレーニングやコンサルティングの専門家でもあります。彼のプロとしてのソフトウェア開発経験は 30年以上に渡り、ここ数年はサーバー側の XML 技術や Java 技術に注力しています。Dennis は、全米各地で行われる会議で頻繁に講演を行っています。また、Java クラスワーキング技術を基に構築された、オープンソースの JiBX XML Data Binding フレームワークの中心開発者でもあります。



2003年 1月 01日

データ・バインディングとは、Javaプラットフォーム用のアプリケーションでXMLを利用するためのシンプルで直接的な方法を提供する技法です。データ・バインディングを使用したアプリケーションは、XML文書の実際の構造を基本的に無視して、データの内容だけをじかに操作できます。もちろん、このような処理がすべてのアプリケーションに適しているわけではありませんが、データ交換を目的としてXMLを利用するごく一般的なアプリケーションでは、この技法が理想的だと言えます。

データ・バインディングには、プログラミングがシンプルになるということ以外にもメリットがいくつかあります。まず、文書の細かな点をかなりの程度無視するので、メモリー内で文書を処理するときに、文書モデルをベースにした手法 (DOMやJDOMなど) に比べて、メモリーの消費量が概して少なくてすみます。また、文書の構造をたどらずにデータにアクセスできるので、文書モデルの手法に比べてプログラム内でのデータ・アクセスが高速になるというメリットもあります。さらに、数字や日付などの特殊なデータ型については、入力時にテキストから内部表現に変換できるので、その種のデータ値をより効率的に処理できるようになっています。

では、データ・バインディングがそれほど優れた技法であれば、文書モデルの手法を採用するケースは存在するのでしょうか。基本的に、2つのケースが考えられます。

  • アプリケーションで文書の細かな構造を処理しなければならないケース。たとえば、XML文書エディターを開発する場合は、データ・バインディングではなく、文書モデルの手法を採用することになるはずです。
  • 処理対象の文書がすべて固定的な構造に基づいているわけではないケース。たとえば、汎用のXML文書データベースを実装する場合には、データ・バインディングは向かないということになります。

データ・バインディングの続編

昨年、筆者は、XML文書とJavaオブジェクトとのマッピングによるデータ・バインディングを活用するためのCastorフレームワークの使用法を取り上げた記事を書きました。その記事の中で、コード生成の手法を取り上げた続編を予告し、JAXBについても取り上げると約束したわけですが(JAXBとは、Java言語によるデータ・バインディング用の標準APIであり、Java Community Process (JCP) で現在開発中)、その記事を発表した直後に、JAXBの方向性に大きな変更を加えるというSun社のアナウンスがありましたJAXBの練り直しを参照)。その変更を踏まえ、完成版に近いJAXBコードがリリースされるまで、続編の執筆を待ったほうがよさそうだと判断していましたが、それがようやく今回実現する運びとなった次第です。

データ・バインディングの用語

以下は、この記事で使っている用語のミニ辞典です。

文法とは、XML文書群の構造を定義した規則集です。XML仕様で定められている文書型定義 (DTD) 形式は、そのような文法の一種です。さらに、W3CのXML Schema仕様で定められているXML Schema形式 (以降、「Schema」) も、文法として徐々に定着しつつあります。具体的には、文書内に記述できる要素や属性を決めたり、要素の入れ子の方法 (入れ子にできる要素の順番や数など) を定めたりするわけです。また、一部の文法 (Schemaなど) ではさらに細かな決まりを盛り込むこともでき、たとえば、特定のデータ型や、場合によっては正規表現さえも、文字データの内容と突き合わせができるようになっています。この記事では、文書群の文法を指す言葉として、正式な「文法」の代わりに「記述」という表現も使っています。

マーシャリングとは、メモリー内のオブジェクトのXML表現を生成する処理のことです。Javaオブジェクトの直列化と同じように、そのXML表現にはすべての依存オブジェクトを組み込む必要があります。つまり、メイン・オブジェクトから参照されているオブジェクトや、その参照されているオブジェクトからさらに参照されているオブジェクトなどをすべて組み込まなければなりません。

アンマーシャリングとは、マーシャリングの逆の処理であり、XML表現からメモリー内にオブジェクトを(場合によってはリンク先オブジェクトのグラフ構造も) 生成します。

この記事では、XML文書の文法からJavaコードを生成するXMLデータ・バインディング用のフレームワークを5つ取り上げます。つまり、JAXB、Castor、JBind、Quick、Zeusの5つです。これらはいずれも無償で入手できるもので、JAXBを除けばすべて、オープン・ソース・プロジェクトとベンダー独自プロジェクトの両方で利用できるようになっています。現時点のJAXB参考実装ベータ版は、評価という用途のためだけにライセンスが与えられていますが、実動リリースが出た時点でそのライセンス形式もおそらく変わるだろうと思います。JAXBとCastorとJBindは、XML文書のSchema記述からコードを生成するのに対し、QuickとZeusは、DTD記述からコードを生成します。このうち、CastorとQuickは、コード生成の代わりになる既存クラスとXMLとのマッピングもサポートしています。

これらのフレームワークにはいずれもメリットとデメリットがあるので、それぞれを個別に取り上げながら、優れた機能や欠点をそれぞれ見ていきたいと思います。このあとの第2回の記事では、サンプル文書を処理したときの各フレームワークのパフォーマンスを取り上げる予定です。また、多くのアプリケーションで必要であるにもかかわらず、既存のデータ・バインディング・フレームワークに欠けている機能があるという点についても触れたいと思っています。

SchemaやDTDの文法からJavaコードを生成する手法には、前回の記事で説明したマッピングによるデータ・バインディングに比べて、大きなメリットがいくつかあります。まず、マッピングによるデータ・バインディングでは、データ・オブジェクトとXML文書の間のリンクを直接的に指定し、構造のすべてのバリエーションをきちんとカバーしたことを自分で確認する必要がありますが、コード生成の場合は、そのリンクが正しく設定されるので、その点の心配が要りません。また、Schemaからコードを生成する場合は、文法で定められているデータ型情報を利用して、正しいデータ型を指定したコードを生成することさえできます。

しかし、コード生成の手法には、いくつかの欠点もあります。まず、アプリケーションのデータ構造とXML文書の構造との間の結合が密になってしまうという点です。また、本当の意味でのオブジェクト・クラスではなく、シンプルなデータ・クラス(つまり、動作が関連付けられていない受動的なデータ・コンテナー)しか処理できない場合もありますし、マーシャリングとアンマーシャリングの処理で、カスタム変換を適用するための柔軟性が限られてしまうかもしれません。コード生成とマッピングのメリット / デメリットについては、あとでさらに詳しく説明します(マッピングとコード生成の比較を参照)。


データとコード

第2回の記事で取り上げるパフォーマンスのテストのために、データ・バインディングの各フレームワークを使ってコードを生成してみました。そのパフォーマンス・テストに使用したのは、架空の航空会社のフライト・スケジュール情報を記述した文書です。ここにサンプル文書の1つを示しますので、構造の雰囲気をまずつかんでおいてください。

リスト1. サンプル文書
<?xml version="1.0"?>
<timetable>
<carrier ident="AR">
<rating>9</rating>
<URL>http://www.arcticairlines.com</URL>
<name>Arctic Airlines</name>
</carrier>
<carrier ident="CA">
<rating>7</rating>
<URL>http://www.combinedlines.com</URL>
<name>Combined Airlines</name>
</carrier>
<airport ident="SEA">
<location>Seattle, WA</location>
<name>Seattle-Tacoma International Airport</name>
</airport>
<airport ident="LAX">
<location>Los Angeles, CA</location>
<name>Los Angeles International Airport</name>
</airport>
<route from="SEA" to="LAX">
<flight carrier="AR">
<number>426</number>
<depart>6:23a</depart>
<arrive>8:42a</arrive>
</flight>
<flight carrier="CA">
<number>833</number>
<depart>8:10a</depart>
<arrive>10:52a</arrive>
</flight>
<flight carrier="AR">
<number>433</number>
<depart>9:00a</depart>
<arrive>11:36a</arrive>
</flight>
</route>
<route from="LAX" to="SEA">
<flight carrier="CA">
<number>311</number>
<depart>7:45a</depart>
<arrive>10:20a</arrive>
</flight>
<flight carrier="AR">
<number>593</number>
<depart>9:27a</depart>
<arrive>12:04p</arrive>
</flight>
<flight carrier="AR">
<number>102</number>
<depart>12:30p</depart>
<arrive>3:07p</arrive>
</flight>
</route>
</timetable>

サンプル文書に対するマッピングによるデータ・バインディングに使用したクラス構造を図1に示します。このあとで、データ・バインディングの各フレームワークを個別に取り上げていきますが、その際に、生成されたクラス構造をそれぞれ比較用として示していきます。いずれの場合も、それぞれの図は単なるサムネールなので、フルサイズのグラフィックを見たい場合は、図をクリックしてください。

図1. マッピングによるデータ・バインディングに使用したクラス構造

クリックして大きなイメージを見る

図1. マッピングによるデータ・バインディングに使用したクラス構造


JAXB完成までの長い道のり

Java API for XML Binding (JAXB) は、Javaプラットフォームのデータ・バインディングに関する新しい規格であり、目下、Java Community Processが「JSR-31 - XML Data Binding Specification」として策定を進めている最中です。このプロジェクトは、XML構造にリンクするJavaコードを生成する方法を定義するという目標で、1999年8月に始まりました。当初は、2000年の第2四半期のリリースを目指していましたが、暫定的なEarly Access (EA) バージョンがアナウンスされたのはJavaOne 2001においてであり、一般に公開されたのは2001年6月のことでした。

JAXBのEAバージョンは、生成されたアンマーシャリング用コードに検証機能を簡単に組み込める、画期的なプル・パーサー設計に基づいており、コード生成のベースとしてDTDを使用し、XML文書の解析時に構造の検証 (データの検証ではない)を自動的に実行するクラスを構築するようになっていました。これはまさに、XMLとJavaオブジェクトの間の変換を処理する高速で効率的な方法として、大きな可能性を示したものですが、このEAコードは部分的な実装にすぎず、完成品と見なされるまでにはさらに多くの作業が必要だったのは明らかです。

作業に当たっていた専門家グループは、すぐにEAバージョンに関するフィードバックを受け取るようになりました。そのフィードバックへの対応という意味も込めて、専門家グループはJAXBの練り直しを始め、その後しばらくして、いくつかの点でJAXBの機能拡張に取り組んでいるというアナウンスをWebサイトに掲載しました。そのアナウンスによれば、次のバージョンは旧バージョンとの間にAPIレベルの互換性を持たないことになったわけです(ただし、EAバージョンそのものは引き続きダウンロードできるようになっていました)。

JAXBの練り直し

新しい方式の詳細が明らかになったのは、2002年3月のJavaOneにおいてでした。そのときのアナウンスでSun社は、JAXB改良の基礎としてEAコードを発展的に廃棄することを発表しました。その代わり、旧バージョンの機能をわずかに残しただけの、互換性のないAPIと内部方式を使用した新しい設計が採用されることになりました。このような劇的な方向転換に驚いたのは筆者だけではありません。EAコードに関心を寄せていた他の開発者たちも、いわば不意をつかれた格好になったわけです。

Sun社のJSRのJAXB担当責任者であるJoseph Fialli氏は、このような大きな変更の要因としていくつかの点を挙げました。一番の問題点は、W3CのXML Schemaをサポートするための旧コードベース拡張作業があまりにも複雑になってしまうということです。このXML Schema仕様の複雑さは悪名高く、仕様として承認されてから2年以上が経過しているにもかかわらず、どのプラットフォームに関しても、ほぼ完全な仕様準拠を実現しているパーサーはほんの一握りしかないというのが実情です。当初のJAXBコードでは、実際のオブジェクトが検証機能を制御する必要があり、その手法をXML Schema仕様に対応させる作業は、妥当な期間内で完成できるようなものではなかったということです。

専門家グループは、XML Schema仕様に対応するための変更のほかに、検証の方法も練り直すことにしました。当初のJAXBコードは、文書の構造を無条件に検証し、エラーが検出された場合は例外を出して、プロセスを終了するという動作になっていました。Fialli氏によれば、この手法はあまりにも単純で制約が多すぎるという意見が一般から寄せられていたようです。要するに、多数の検証エラーを一挙にチェックしたい場合もあれば、検証を完全にオフにしたい場合もあるわけです(検証を完全にオフにするケースとしては、パフォーマンスを優先したい場合や、文法に完全に合致しない文書をマーシャリングする場合などがあるでしょう)。新しいJAXB方式では、この両方の処理を実現しています。

さらに、当初のEAバージョンでは、データ・バインディングの実行環境として1つのフレームワークだけを想定していましたが、専門家グループはこの考え方も改めることにしました。つまり、インターフェース方式を採用して、舞台裏でさまざまなデータ・バインディング・フレームワークを利用できるようにしたわけです。そのため、ユーザー・コードはフレームワーク間で移植できるようになっていますが、生成されたクラスの移植はできません。生成されたクラスは1つのデータ・バインディング・フレームワークに固有のものであり、そのフレームワークでしか動作しません。こうして、EAバージョンの実行環境で使用されていたプル・パーサー方式は破棄され、必須のSAX2 2.0パーサー・サポートと、他の代替パーサーに対応したフレームワーク固有のオプショナル・サポートが採用されることになりました(その種のオプショナル・サポートとして、新しいプル・パーサーに基づいたStreaming API for XMLも組み込まれる可能性があります。このStreaming API for XMLについては、JSR 173による策定作業が進められています)。また、データ構造そのものも、外部フレームワークから簡単に操作できるJavaBeanスタイルのデータ・オブジェクトに変更されています。

JAXBベータ版

昨年の3月以降、JAXBプロジェクトはこの新しい方針に沿って進められてきました。その作業の最初の成果は、昨年の夏の終わりに新しい暫定版のドラフト仕様として公開されました。その後10月になって、Sun社は新しいベータ版のJAXB参考実装を発表し、廃棄が決まって久しいEAバージョンがついに消えることになりました。筆者がこの連載記事の評価テストとパフォーマンス・テストに使用したのは、この最新のベータ版です。このベータ版は文書のSchema記述をじかに処理して、文書に定義されている要素のデータ型や使用法に合致したクラス構造を生成します。このクラス構造には、大きく分けて4種類のクラスが組み込まれます。つまり、定義されているデータ型に対応したインターフェース、実際の要素に対応したインターフェース、それら2つのインターフェースのそれぞれの実装という4種類のクラスです。

図2. JAXBのインターフェース・クラス構造

クリックして大きなイメージを見る

図2. JAXBのインターフェース・クラス構造

図3. JAXBの実装クラス構造

クリックして大きなイメージを見る

図3. JAXBの実装クラス構造

これらのクラスのうち、アプリケーション・プログラミングの観点からして一番面白いのは、データ型に対応したインターフェースでしょう。このインターフェースは、該当するデータ型のデータを操作するgetメソッドとsetメソッドの、JavaBeanスタイルのコレクションです。データ型に対応したインターフェースに用意されているメソッドは、JAXB仕様で定められている規則に準拠しているので、アプリケーション・コードからすべてのデータにアクセスするためにこのインターフェースを利用しても何の問題もありませんし、その一方でJAXB実装間の移植性をそのまま維持することもできます。この種のインターフェースを利用すれば、既存の文書に対して簡単に実行できるコードをJAXBから生成できます。ただし、データ構造を構築したり変更したりするときには、少々物足りなさを感じるかもしれません。要するに、インターフェースを利用するということは、インスタンスをじかに作成できないという意味です。つまり、ファクトリー・メソッドによってインスタンスを作成し、JavaBeanスタイルのアクセサー・メソッドによってインスタンスにデータ値を取り込む必要があるわけです。

JAXBの場合、Schema記述からコードを生成する処理は非常にシンプルです。用意されているxjc というバインディング・コンパイラーは、コマンド行ツールとして動作します。要は、Schema文書を入力として使用し、指定した出力パッケージと宛先ディレクトリにファイルを生成するわけですが、生成されたコード・ファイルを読み取り専用にするかどうかや、Schema記述を厳密に検証するかどうかを指定するためのオプションもあります。

JAXB仕様では、コード生成によるデータ・バインディングの各種動作をバインディング宣言によってカスタマイズするための方法も定めています。たとえば、次のようなオプションがあります。

  • 生成されたクラスとプロパティーの名前を制御するためのオプション
  • バインディングに使用する既存の実装クラスを指定するためのオプション
  • 検証機能や、マーシャリング / アンマーシャリングに使用する直列化 / 直列化解除機能を(限定的ながら) 制御するためのオプション

このようなカスタマイズ指定は、実際のSchema文書内に注釈という形で埋め込むこともできれば、別個の外部バインディング宣言文書という形で用意することもできます。現在のベータ版の参考実装では、Schema文書内の注釈という方法しかサポートされていませんが、将来のリリースでは、外部バインディング宣言文書による方法もサポートされる予定です。

全体的に考えれば、JAXBはまさに、W3CのXML Schemaの文法で定義された文書にJavaコードをバインディングするための強力で柔軟なツールとして確実に発展を遂げていると言えます。Javaプラットフォームの規格としておそらく承認されるでしょうから、これから幅広くサポートされていくのは間違いなさそうです。そうなれば、各種の実装間でデータ・バインディング・アプリケーションを移植するのは、各種のサーブレット・エンジン間でWebアプリケーションを移植するのと同じほど簡単になるはずです(ただし、基本的には非常にシンプルですが、時としてややこしい場合もあるかもしれません)。

とはいえ、JAXBには欠点もあります。現時点で最大の制約になっているのは、評価以外の用途についてはライセンスが与えられていないという点です。今のところ、実動リリースの発表はこの四半期内に予定されていますが、それまでの間は、JAXBを実際のプロジェクトで使用することはできません。また、生成コードに適用できるカスタマイズの程度にも制約があります。JAXBで定義されているインターフェースに対応した独自の実装クラスを用意できるケースも確かに多くあるでしょうが、インターフェース自体はどんな場合にもSchema記述と結びついており、変更する余地はほとんどありません。


Castor

XMLデータ・バインディング用のCastorフレームワークは、マッピング方式とコード生成方式の両方をサポートしています。前回の記事では、Castorのマッピング方式によるデータ・バインディングの機能をいくつか取り上げました。それで、今回はSchema記述からのコード生成だけについて説明したいと思います。ちなみに、このあとの第2回では、両方の方式のパフォーマンスについて書く予定です。Castorのマッピング方式によるデータ・バインディングの詳細については、前回の記事をお読みください(参考文献を参照)。

図4. Castorで生成されるクラス構造

クリックして大きなイメージを見る

図4. Castorで生成されるクラス構造

JAXBに比べると、Castorのコード生成方式のサポートは細かな点がかなり違いますが、基本的な意図はほとんど同じです。要するに、JAXBの場合と同じようにJavaBeanスタイルの構造を持ったデータ・モデルをアプリケーションに渡します。主な違いは、Castorではインターフェースを使用せずに、生成される実装クラスをじかに処理するということです。それぞれの実装クラスのほかに、Castorでは、バインディング・コードと検証コードを組み込んだディスクリプター・クラスも生成します。また、Castorはインターフェースの代わりに具体クラスを使用するので、文書のデータ構造を構築したり変更したりする処理が、JAXBよりもいくらかシンプルになります。該当するクラスに対応したコンストラクターをじかに使用できるので、ファクトリー・クラスを経由する必要がありません。

現在のベータ版のCastor (この記事の執筆時点では0.9.4.1) は、コード生成のカスタマイズと言えるような機能をまったくサポートしていませんが、これは今後変わっていくはずです。次のベータ版では、コード生成のいろいろな機能を制御するためのマッピング・ファイルを使用できるようになると予想されています。このようなカスタマイズは、まずはクラス名やパッケージ名だけに限られるでしょうが、長期的に見れば、ユーザーが独自の実装クラスを用意できるようにもなるようです。さらに、CastorでJAXBをサポートする計画もあるようで、その場合には一種の互換層のようなものを使うことになるのかもしれません。

Schema記述からコードを生成する処理について言えば、Castorの場合もJAXBと同じほどシンプルであり、基本的なオプションも同じです。ただし、Castorでは、JAXBにないコマンド行パラメーター・オプションがいくつか用意されており、プロパティー・ファイルの設定値という形でさらに多くのオプションを指定できるようにもなっています。とはいえ、これらは基本的に特殊な状況で役立つオプションであり、クラス名や検証の制御という点では、Schema文書の注釈としてJAXBに用意されている機能に及びません。

現時点でCastorによるソース・コード生成の主な欠点と言えるのは、カスタマイズのサポートが限られているということです。ただし、これは変わりつつありますし、Castorのマッピング方式によるデータ・バインディングで可能なカスタマイズの機能からすれば、少なくともそれと同程度の柔軟性がやがてソース・コード生成方式でも可能になると期待できます(Castorのマッピング方式によるデータ・バインディングについては、前回の記事で取り上げたとおりです。参考文献をご覧ください)。そうなれば、長い目で見て、JAXBよりも柔軟性が高くなっていくのは間違いありません。

CastorはBSDスタイルのライセンスでリリースされており、特に大きな制限もなく、完全な商業ベースでの利用が可能になっています。安定性もまずまずのレベルになっているようですが、修正を必要とするバグがあった場合は、最新の開発コードにアップデートするか、新しいベータ版が出るのを待つことが必要になります。


JBind

JAXBやCastorと同じように、JBindもXML文書のSchema記述に基づいてバインディング・コードを生成します。この点は共通していますが、JBindの中心的な発想は、実際にはほかの2つとはかなり異なっています。開発チームの責任者であるStefan Wachter氏は、その発想を称して「XMLコード」と呼んでいます。つまりこれは、Schemaによって記述されているXMLデータと、Javaコードによって実現される処理の組み合わせという意味です。JAXBとCastorは、Javaアプリケーションによって簡単にXMLを処理しようという発想ですが、JBindは、XMLの周りにアプリケーション・コードのフレームワークを作ろうという発想になっているわけです。そのフレームワークをJBindによって作ってしまえば、独自のコードによってそのフレームワークを拡張し、機能を追加していくことができます。

図5. JBindで生成されるクラス構造

従来型のデータ・バインディングにJBindを利用することも一応可能です。現に、筆者自身も第2回の記事で取り上げるパフォーマンス・テストのために、従来型の手法でJBindを使ってみたわけですが、実際にはいくぶん不便なところもあります。その理由の1つは、文書のSchema記述をどうしても実行時に処理しなければならないという点です。したがって、インスタンス文書がSchema記述を直接参照していない場合は、ある種のマッピング・ファイルを使用するか、インスタンス文書を読み取る前に独自のコードにSchema記述を手作業で読み込む必要があります。現在用意されている説明資料には、実際にそのしくみに関する記述がありません。また、バインディング先の文書構造の変更を処理するという点でも、ほかのデータ・バインディング・フレームワークに比べて柔軟性が低いと言えます。既存の要素オブジェクトを削除する場合は、ListIterator を使って対応できますが、新しい要素オブジェクトを作成する場合は、生成された作成用メソッドを使う必要があり、既存の内容の末尾に新しい要素オブジェクトが自動的に追加されてしまいます。

JBindは舞台裏で、文書データを処理するための非常に異質な方法を使っています。JAXBやCastorのようにJavaBeanスタイルのデータ・クラスを生成する代わりに、すべてのデータを文書モデル (現時点ではDOMレベル2の実装) に格納し、その文書モデルに格納したデータにアクセスするための手段としてバインディング・コードを生成します。これは非常に面白い着想であり、十分に発展させていけば、パラダイム横断的な革新をもたらす可能性もありそうです。現時点では、生成コードで XPathを使った制約とアクセス・メソッドが使えるというのが唯一のメリットですが、この格納メカニズムがJBindの本質から切り離せないものである以上、こうした現状も今後変わっていくことでしょう。

ここで取り上げているデータ・バインディングのフレームワークの中で、JBindはSchemaとXPath拡張機能 (上記)のサポートが最も充実しているという強みがあります。XML文書の処理を中心としたアプリケーションを開発する場合は、JBindが作り出す「XMLコード」フレームワークによって、アプリケーションの操作性が大幅に向上するかもしれません。一方、データ・バインディングの一般的な用途については、つまり、XML文書の処理がアプリケーションの中心機能でない場合は、その他のデータ・バインディング方式のほうがシンプルになりそうです。さらに、アンマーシャリング時に必要な検証と、バックエンドの文書モデル格納メカニズムのために、その他のフレームワークに比べて、パフォーマンスが落ちるという欠点もあります(この点については、第2回の記事でさらに詳しく取り上げます)。JBindは、完全な商業ベースの使用を認めるApacheスタイルのライセンスで配布されています。


Quick

Quickの説明資料を読むと、QuickはXMLを処理するためのツールではなく、XMLを使用するためのJava言語の「拡張機能」という位置付けになっています。現に、このフレームワークの開発作業は、JavaプラットフォームやXMLよりも前にさかのぼり、これまで数多くの練り直しを経てきました。確かに、JavaプラットフォームでXMLを処理するための非常に柔軟なフレームワークであり、この記事を書くために筆者自身も勉強や実験を重ねてきましたが、それでもつかみきれない事柄が多々あります。

図6. Quickで生成されるクラス構造

クリックして大きなイメージを見る

図6. Quickで生成されるクラス構造

Quickのこうした柔軟性には、その一方で犠牲も伴います。DTD文書記述から生成コードに至るまでには、かなり複雑な手順が必要になっており、中間ステップとして以下の3種類のバインディング用スキーマ文書(W3CのXML Schemaとは違う) を使用します。

  • QDML文書。ほぼDTDに近い文書記述ですが、データ型と継承の情報が追加されています。
  • QJML文書。XMLからJavaオブジェクトに対するバインディングを定義した文書です。
  • QIMLファイル。基本的にはQJMLをコンパイルしたファイルであり、実際のバインディング・コードを生成するときに使用します。

第2回の記事で取り上げるQuickのパフォーマンス・テストでは、これらのファイルのカスタマイズを最小限に抑えましたが、それでも目的の最終結果を得るには多少の編集を手作業で加える必要がありました。まずDTD文法からQDMLファイルを生成した後、そのファイルを編集して、文書のルート要素を定義し、String 以外の値 (この場合は、いくつかのint) についてデータ型情報を追加しました。さらに、プログラムを実行してQDMLからQJMLファイルを生成した後、そのQJMLを編集して、参照にデータ型の情報を追加しました。この作業は必須というわけではありませんが、実際にこの作業をしておくと、オブジェクト参照のデータ型を指定したコードを生成できます(これは、CastorやJAXBのコード生成ではサポートされていない機能です)。最後に、プログラムを実行してQJMLからQIMLファイルを生成してから、コード生成用ツールを実行して、JavaBeanスタイルのオブジェクト・クラスと、XMLとの変換に使用する実際のバインディング・クラスを生成したわけです。

これらのファイルにもっと多くの編集を手作業で加えれば、オブジェクト・クラスの新しいコードを生成する代わりに、Castorのマッピング方式のバインディングで使用した既存のクラスにじかにリンクするというやり方もできたはずです。このように既存のクラスを処理できるというのは、非常に強力な機能です。確かにQuickのこのような利便性も、スキーマ・ファイルの複雑さや、その機能を利用するために必要な編集作業の量によってやや色あせてしまいますが、Quickの柔軟性が非常に高いということだけは間違いありません。

このような柔軟性はQuickの最も強力な特色です。一方、Quickの主な欠点は、各種スキーマ・ファイルが複雑だという点と、W3CのXML Schema文法のサポートがないという点です。また、Quickの使用に関する支援が得にくいという問題もあるようです。フォーラムでもメーリング・リストでも、質問に対する回答が得られないケースがしばしばあるからです。Quickのライセンスは、GNU LibraryまたはLessor General Public License (LGPL)に基づいており、基本的にフリーのプロジェクトにも商業ベースのプロジェクトにも、ソフトウェアを組み込めるようになっています。


Zeus

Quickと同じように、ZeusもXML文書のDTD記述に基づいてコードを生成します(W3CのXML Schemaのサポートについても作業が進められていますが、現時点ではまだ開発の第1段階の前の段階です)。この2つのフレームワークの共通点はこれくらいしかありません。Quickは複雑で強力ですが、Zeusはシンプルで機能がごく限られているからです。

図7. Zeusで生成されるクラス構造

クリックして大きなイメージを見る

図7. Zeusで生成されるクラス構造

Zeusのコード生成の方法は、JAXBやCastorとよく似ていて、必要なクラスを生成するためのコマンド行ツールが用意されています。また、JAXBと同じように、バインディングにはインターフェースを使います。ただし、JAXBでは、ファクトリーを使ってオブジェクト・クラスの新しいインスタンスを生成するのに対し、Zeusでは、生成された実装クラスとプロトタイピングの手法を利用します。つまり、実装クラスのサブクラスを作成してから、文書のアンマーシャリング時に、もともと生成された実装クラスではなく、そのサブクラスを使用するわけです。

ここで取り上げている他のフレームワークとは異なり、ZeusはString だけをサポートしており、intDate などのデータ型付きの値をサポートしていません。また、参照もサポートしていないため、グラフ構造をじかにマーシャリング / アンマーシャリングすることもできません。データ・バインディングの利便性は、基本的に、データ型付きの値を処理できるという点と、オブジェクト間のリンクを透過的に扱えるという点にあるわけなので、これらは大きな制約になります。こうした機能をサポートしていないZeusは、正真正銘のデータ・バインディング・フレームワークというよりは、機能を切り落とした文書モデルという感じがします。

それでも、String データ型の値だけを処理する場合は、Zeusも選択肢の中に入ってくるかもしれません。一方、Zeusの主な欠点は、バインディングの機能がごく限られているという点と、プロジェクトの全体的な進展速度が遅いという点です。Quickと同じように、質問に対する回答もなかなか得られません。Zeusは、Mozillaパブリック・ライセンスから派生したEnhydraパブリック・ライセンスのバージョン1.1に基づいて配布されています。


マッピングとコード生成の比較

この記事では、XML文書の文法からJavaコードを生成するための各種フレームワークを取り上げてきました。このコード生成という手法は、Javaアプリケーション用のXMLデータ・バインディングを処理する1つの方法にすぎません。もう1つの主な方法は、マッピングによるデータ・バインディングです。この場合は、独自のクラスを作成し(または、まず文法からクラスを生成した後でニーズに合わせてそのクラスを修正し)、データ・バインディング用のフレームワークに対して、クラスとXML文書との間の関連付けを指定することになります。どちらの手法にもメリットとデメリットがあり、それぞれの状況に合わせてどちらの手法を使ったらよいかを判断できると思います。

コード生成では、XML文書の構造 (つまり、DTDやSchemaなどの形で記述した文法)を反映したクラスを自動的に生成するため、文書の処理にすぐに取りかかれます。コード生成がSchema記述に基づいている場合は、生成するクラスに完全なデータ型の情報を組み込むことができます(ただし、これには問題もあります。Schemaのデータ型とJava言語のデータ型との間に直接的な対応関係が存在しないということです)。さらに、コード生成では、生成されるクラスの中に検証機能を組み込むこともできます。値の設定時に自動的に値のチェックを実行するやり方もあれば、必要に応じて検証を実行するやり方もあります。したがって、マーシャリングで生成する文書が、該当する構造に必ず合致することを確実に保証できるわけです。

コード生成の主な欠点は、要するに長所の裏返しと言えます。文書構造を厳密に反映するということは、アプリケーション・コードと文書構造との間の結合を密にしてしまうということでもあります。文書構造に変更があった場合は、コード生成を実行し直してから、修正後のデータ・クラスに合わせてアプリケーション・コードを書き直さなければなりません。

また、コード生成の場合は、基本的に文書構造全体を処理する必要があり、文書構造のサブセットを作成するのは容易ではありません。たとえば、業界標準として定義されているような数多くのオプショナル・コンポーネントを組み込んだ複雑な構造があるとしましょう。ところが、それらのコンポーネントのごく一部だけを利用した文書を処理するアプリケーションを開発する場合は、この点が問題になります。今回取り上げたほとんどフレームワークでは、生成されるクラスは必ず構造全体に合致したものになりますし、一部のフレームワークでは、生成されたクラスをすべて実行時に組み込む必要もあります。その場合は、コードが膨大になり、アプリケーションの用途に対してデータ・モデルが複雑すぎる結果になってしまいます。もちろん、DTDやSchemaの記述を編集して、不要なコンポーネントを除外すればこの問題は解決できますが、今度は基本文法に変更があるたびに修正記述を改訂していかなければならないという、新たな問題が発生してしまいます。

マッピング方式のデータ・バインディング(CastorやQuickで実装されている方式) の場合は、コード生成方式よりも柔軟性が高くなります。この方式では、データと動作を組み合わせた本当の意味でのオブジェクト・クラスを処理することになります。また、オブジェクト・クラスを実際のXMLからある程度切り離すことができます。したがって、XML文書の構造に小さな変更があった場合は、アプリケーション・コードを書き直すまでもなく、マッピングの定義を修正するだけで対応できます。さらに、入力時のマッピングと出力時のマッピングを別々に用意して、文書のマーシャリング時とアンマーシャリング時のマッピングをそれぞれ変えることさえ可能です。一方、マッピング方式のデータ・バインディングの欠点は、コード生成方式に比べてセットアップに手間がかかるという点です。

要するに、どちらの方式も、すべてのアプリケーションに完全に対応できるものではありません。SchemaやDTDの記述で定義されている安定した文書構造を処理していて、その構造がアプリケーションのニーズにも適合しているのであれば、コード生成方式のほうが適しているでしょう。一方、既存のJavaクラスから作業を進める場合や、XML文書の構造の代わりにアプリケーションのデータ使用を反映したクラス構造を利用したい場合は、マッピング方式のほうが優れています。残念ながら、現在の開発プロジェクトのほとんどは、マッピング方式ではなくコード生成方式のほうに目を向けているようです。したがって、現時点でマッピング方式に対応しているのは、CastorとQuickだけになっています。


結論

この第1回の記事では、XML文書記述からJavaコードを生成するための、XMLデータ・バインディングの主なフレームワークを取り上げました。これらのフレームワークの機能性はそれぞれ大きく異なっています(さらに、第2回の記事で取り上げるように、パフォーマンスもそれぞれ異なります)。W3CのXML Schema定義に基づく方式の中では、現時点でCastorが最も充実した汎用のデータ・バインディング・アプリケーション・サポートを提供しています。Castorはすでに使用が可能になっており、今後は生成コードのカスタマイズ機能やSchemaサポートを充実させていくと予想されます。また、これから正式に登場するJAXB規格もサポートすると思われます。

JAXBは、実動リリースが登場した時点で非常に魅力的なオプションになることでしょう(現在のベータ版のライセンスでは、評価のための使用しか認められていません)。現時点では、JAXBよりもCastorのほうが充実したカスタマイズ機能をサポートすると予想されますが、JAXBには実装間の移植性という強みがあります。また、JAXBはJavaプラットフォームの他の規格(Webサービス用のJAX-RPC規格など) の内部で使用されるようになると思われます。そうなれば、JAXBに準拠したアプリケーションを開発するということは、今後その種の規格とのプラグインによる互換性を確保できるという意味にもなるわけです。

JBindは、Schemaのサポートという点では最も機能が充実しています。アプリケーションが「XMLコード」モデルに適合し、パフォーマンスに関する厳しい要件がないのであれば、JBindは有力な選択肢になるかもしれませんが、XMLデータ・バインディングの一般的な用途にはあまり向かないようです。Quickは、柔軟性が非常に高く強力ですが、DTDの文法しかサポートしておらず、操作がかなり複雑です。Zeusはシンプルで簡単に操作できるのが特徴ですが、Quickと同じくDTDしかサポートしていないばかりか、String データ型の値しか処理できません。

後半の3つは、一般的な用途というよりも、特殊な要件を持ったアプリケーションに適していると言えます。たとえば、文書のDTD記述だけがあってスキーマがないのであれば、QuickやZeusを試しているとよいかもしれません。文書のDTD記述だけがあるという状況は、ほとんどのアプリケーションでは大きな問題になりません。DTDをスキーマに変換するためのプログラムが数多く出回っているからです(とはいえ、手作業による多少の編集が必要な場合もあります)。そのようなプログラムの1つがCastorディストリビューションに組み込まれています(Castor XML FAQで指摘されているorg.exolab.castor.xml.dtd.Converter です)。

第2回の記事では、サンプル文書に対してこれらのデータ・バインディング・フレームワークをテストしたときのパフォーマンス結果を示したいと思っています。データ・バインディングのコード生成方式とマッピング方式の結果はもちろん、比較のために文書モデルの結果も合わせて紹介する予定です。それにしても、その結果には筆者自身も驚きました (…が、ここでは書きません)。詳細については、「請うご期待」です。カレンダーの来週の日付に印をつけておき、パフォーマンスに関する次回の記事をすぐにご覧ください。読みごたえのある内容になるのは間違いありません。

参考文献

  • この著者の前回の記事「Castorによるデータ・バインディング」をお読みください。Castorによるマッピング方式のデータ・バインディング技法を取り上げています (developerWorks、2002年4月)。
  • この著者による以前のdeveloperWorks 記事で、Java XML文書モデルの、performance (September 2001年9月)、およびusage (2002年2月) の比較を検討してください。
  • Brett McLaughlin氏のQuick関連記事 「QuickによるJavaオブジェクトとXMLの変換」をお読みください。Quickフレームワークを使って、Javaデータを簡単にXML文書に変換する方法を取り上げています。この場合は、他のデータ・バインディング・フレームワークとは違って、クラス生成セマンティクスは必要ありません(developerWorks、2002年8月)。
データ・バインディング・フレームワーク
  • Java Architecture for XML Binding (JAXB) の詳細をお調べください。これは、Javaプラットフォームのデータ・バインディングに関する新しい規格です。
  • Castor フレームワークの詳細をご覧ください。このフレームワークは、マッピング方式とコード生成方式の両方のデータ・バインディングをサポートしています。
  • JBind についてお読みください。このフレームワークは、Javaアプリケーションによって簡単にXMLを処理しようという発想ではなく、XMLの周りにアプリケーション・コードのフレームワークを作ろうという発想に基づいています。
  • Quick フレームワークの開発作業は、JavaプラットフォームやXMLよりも前にさかのぼります。これは、JavaプラットフォームでXMLを処理するための非常に柔軟なフレームワークです。
  • Zeus の詳細をご覧ください。Quickと同じように、XML文書のDTD記述に基づいてコードを生成しますが、Quickよりも操作が簡単で、機能が限られています。
他のリンク

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML
ArticleID=243232
ArticleTitle=JavaにおけるXML: データ・バインディング 第1回: コード生成方式 -- JAXBなどなど
publish-date=01012003