QuickによるJavaオブジェクトとXMLの変換

JavaオブジェクトとXMLデータの統合

Quickは、実行時の変換に重点を置いた、オープン・ソースのデータ・バインディング・フレームワークです。この記事では、他のデータ・バインディング・フレームワークで必要になるクラス生成セマンティクスを使わずに、このフレームワークを使用してすばやく問題なくJavaデータをXML文書に変える方法について示します。適用範囲の広いコード・サンプルも含まれています。

Brett D. McLaughlin, Sr., Author and Editor, O'Reilly Media, Inc.

Photo of Brett McLaughlinBrett McLaughlin氏は、Logo (小さな三角形を覚えていますか?) の時代からコンピューターの仕事をしています。現在の専門は、JavaおよびJava関連のテクノロジーを使ったアプリケーション・インフラストラクチャーの構築です。ここ数年は、Nextel Communications and Allegiance Telecom, Inc. でこれらのインフラストラクチャーの実装に携わっています。Brett氏は、Javaサーブレットを使ってWebアプリケーション開発のための再利用可能なコンポーネント・アーキテクチャーを構築するJava ApacheプロジェクトTurbineの共同設立者の1人です。同氏はまた、オープン・ソースのEJBアプリケーション・サーバーであるEJBossプロジェクトと、オープン・ソースのXML Web公開エンジンであるCocoonにも貢献しています。



2002年 8月 01日

XMLはこの数年で確実にプログラミングの世界を席巻しました。しかし、もともと大きかったXMLアプリケーションの複雑さは、これまでそれほど減少しませんでした。デベロッパーは依然として、XMLの複雑なセマンティクスやXMLを操作するためのAPI (SAXやDOMなど) の学習に (数か月ほどでなくても) 数週間を費やさなければなりません。しかし、ここ6~12か月のうちに、Quickと呼ばれるXML APIの新しいクラスが、他の複雑なAPIより単純な選択肢として人気を得るようになりました。

データ・バインディングを行えば、XMLの属性やエレメントを扱わずに、JavaオブジェクトとXMLの間を直接マップできます。さらに、Javaデベロッパーは、何時間もかけてXMLの仕様を頭に詰め込まなくても、XMLを使った作業を行なえます。Quickはそのようなデータ・バインディングAPIの1つです。このプロジェクトはJavaアプリケーションでビジネス利用を行なうことを目的としています。

インストールとセットアップ

Quickの使用方法について詳しく考える前に、このプロジェクトをダウンロードしてインストールする必要があります。QuickのWebサイト (参考文献を参照) にアクセスし、Download (ダウンロード) を選択してください。ここで、このプロジェクトの .zipファイルをダウンロードできます。この記事の作成時点で使用できる最新バージョンはQuick 4.3.1で、Quick4.3.1.zipファイルからアクセスできます。

.zipファイルを解凍して、Quickディストリビューションを作成します。ディレクトリー階層はリスト1 のとおりです。

リスト1. Quickのディレクトリー構造
Quick4
  |
  +-- JARs
  +-- BATs
  +-- Doc
  +-- dtdParserSrc
  +-- DTDs
  +-- examples
  +-- JARs
  +-- QDMLs
  +-- QJMLs
  +-- quickSrc
  +-- UTILs
  +-- utilSrc
  +-- XSLs

デベロッパーに最も関係ある2つのディレクトリーは、Quick4/BATs (PATH 環境変数に追加する必要があります) とQuick4/JARs (jar ファイルが含まれています。CLASSPATH 環境変数に追加する必要があります) です。特に、dtdparser115.jarQuick4rt.jar、およびQuick4util.jar を現在のCLASSPATHに追加する必要があります。また、ApacheプロジェクトのXerces-J (参考文献を参照) などのSAXパーサー・インプリメンテーションも必要です。xerces.jar か他のお気に入りのパーサーを同様にCLASSPATHに追加してください。


JavaクラスとXML文書

データ・バインディングの中心になるのはXMLとJavaなので、これらのXML文書とJavaクラスがどのようにQuickに関連しているかを考えます。これらの点を示すため、ここではいくつかの単純なJavaクラスと単純なXML文書を使用します。

単純なXML文書

まず、リスト2 は小さなXML文書を示しています。10~15個ものJavaクラスに埋もれて概念を見逃すことがないよう、単純な構造になっています。

リスト2. 人を表すXML
<?xml version="1.0"?>
<!DOCTYPE person SYSTEM "person.dtd">
<person>
  <firstName>Gary</firstName>
  <lastName>Greathouse</lastName>
  <address type="home">
    <street>10012 Townhouse Drive</street>
    <city>Waco</city>
    <state>TX</state>
    <zipCode>76713</zipCode>
  </address>
  <phoneNumber>
    <type>home</type>
    <number>2545550287</number>
  </phoneNumber>
  <phoneNumber>
    <type>work</type>
    <number>2545556127</number>
  </phoneNumber>
</person>

リスト2 は、XMLの作成方法を示す例としては優れていませんが、Quickの注目に値するいくつかの点を示しています。この文書のDTDについて知りたい場合は、リスト3 を参照してください。

リスト3. person.xmlのDTD
<!ELEMENT person (firstName, lastName, address+, phoneNumber+)>
<!ELEMENT firstName (#PCDATA)>
<!ELEMENT lastName (#PCDATA)>
<!ELEMENT address (street, city, state, zipCode)>
<!ATTLIST address
          type    (home | work | other)    "home"
>
<!ELEMENT street (#PCDATA)>
<!ELEMENT city (#PCDATA)>
<!ELEMENT state (#PCDATA)>
<!ELEMENT zipCode (#PCDATA)>
<!ELEMENT phoneNumber (type, number)>
<!ELEMENT type (#PCDATA)>
<!ELEMENT number (#PCDATA)>

Javaクラス

ここで、多くのデータ・バインディング・インプリメンテーションでは、このタイプのXML文書を表すJavaソース・ファイルを生成しなければなりません。その種のインプリメンテーションでは、使用するJavaビジネス・オブジェクトが存在していないという一般に誤った想定がなされています。より一般的には、XMLの作業を始めるために必要なJavaクラスのセットが存在しています。この事例こそ、Quickがまさに役立つ事例と言えます。このことを念頭に置いて、人を表す3つのJavaソース・ファイル (リスト45、および6 ) を見て下さい。

リスト4. Personクラス
import java.util.LinkedList;
import java.util.List;
public class Person {
    /** The first name of the person */
    private String firstName;
    /** The last name of the person */
    private String lastName;
    /** The addresses of the person */
    private List addressList;
    /** The phone numbers of the person */
    private List phoneNumberList;
    public Person() {
        addressList = new LinkedList();
        phoneNumberList = new LinkedList();
    }
    public Person(String firstName, String lastName,
        List addressList, List phoneNumberList) {
            this.firstName = firstName;
        this.lastName = lastName;
        this.addressList = addressList;
        this.phoneNumberList = phoneNumberList;
    }
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public List getAddressList() {
        return addressList;
    }
    public void setAddressList(List addressList) {
        this.addressList = addressList;
    }
    public void addAddress(Address address) {
        addressList.add(address);
    }
    public List getPhoneNumberList() {
        return phoneNumberList;
    }
    public void setPhoneNumberList(List phoneNumberList) {
        this.phoneNumberList = phoneNumberList;
    }
    public void addPhoneNumber(PhoneNumber phoneNumber) {
        phoneNumberList.add(phoneNumber);
    }
}
リスト5. Addressクラス
public class Address {

    /** The type of address */
    private String type;
    
    /** The street address */
    private String street;
    
    /** The city */
    private String city;
    
    /** The state */
    private String state;
    
    /** The zip code */
    private String zipCode;
    
    public Address() { }
    
    public Address(String type, String street, String city, 
        String state, String zipCode) {
        
        this.type = type;
        this.street = street;
        this.city = city;
        this.state = state;
        this.zipCode = zipCode;
    }
    
    public String getType() {
        return type;
    }
    
    public void setType(String type) {
        this.type = type;
    }
    
    public String getStreet() {
        return street;
    }
    
    public void setStreet(String street) {
        this.street = street;
    }
    
    public String getCity() {
        return city;
    }
    
    public void setCity(String city) {
        this.city = city;
    }
    
    public String getState() {
        return state;
    }
    
    public void setState(String state) {
        this.state = state;
    }
    
    public String getZipCode() {
        return zipCode;
    }
    
    public void setZipCode(String zipCode) {
        this.zipCode = zipCode;
    }
}
リスト6. PhoneNumberクラス
public class PhoneNumber {
    /** The number itself */
    private String number;
    /** The type of number */
    private String type;
    public PhoneNumber() { }
    public PhoneNumber(String type, String number) {
        this.type = type;
        this.number = number;
    }
    public String getNumber() {
        return number;
    }
    public void setNumber(String number) {
        this.number = number;
    }
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
}

これらのクラスは、かなり自明です。前にリスト2 で見たXML構造に対応しています。おそらく、XML文書をこれらのJavaオブジェクトのインスタンスに変換したり、元に戻したりしたいと思われるでしょう。Quickを使えば、このような変換は朝飯前です。


初期化

XML文書 (複数も可)、DTD、およびJavaクラスを用意できたら、Quickフレームワークに一方の形式から他方の形式へのマップ方法を伝える必要があります。これは、次のような複数のステップからなるプロセスです。

  1. XML文書のDTDからQDMLファイルを作成する
  2. QDML文書からQJMLファイルを作成する
  3. QJML文書からQIMLファイルを作成する
  4. QIMLを使用してJavaをXMLにマップし、元に戻す

次のいくつかのセクションで、各ステップを順番に行う方法について示します。Quickを動かすには多くのステップが必要なように思えるかもしれませんが、各ステップは1回だけ行えばよいので、Quickフレームワークを使用するための先行投資のように見なすことができます。いったんこれらのステップを行ったら、このプロセス・オーバーヘッドを起こさずに、アプリケーションでQuickを好きなだけ使用できます。

QDMLの作成

Quickを動かすために最初に行わなければならないのは、QDMLファイルを作成することです。QDML (Quick Document Markup Language) は実質的にQuickバージョンのDTDであり、Quickフレームワークで使用するXML文書の構造を定義します。この時点では、マッピング情報を提供しません。Quickが理解できる形式で文書を定義するだけです。もちろん、これはQuickのツールによって行うので、デベロッパーの生活は楽になります。

まず、インストールとセットアップで示されているとおりにCLASSPATHがセットアップされていることを確かめます。次に、QuickディストリビューションのBATs ディレクトリーにあるcfgDtd2Qdml スクリプトを使用します。WindowsユーザーはcfgDtd2Qdml.bat を、UNIXユーザーはcfgDtd2Qdml.sh を使用します。(この記事の例はすべてUNIX形式ですが、Windowsでも容易に同じことを行えます。)

以下のコマンドを発行します。

sh cfgDtd2Qdml.sh -in=person.dtd -out=person.qdml

興奮するような出力があるわけではありませんが、新しいファイルperson.qdml が得られます。これで、DTDがQuickにおいてより容易に理解される形式になったので、あと少しで次のステップに進むことができます。

次に進む前に、Quick (および使用するQDMLファイル) に、XML文書のルート・エレメントを伝える必要があります。この例の場合、ルートはperson エレメントです。この作業を行うには、以下のように別のQuickユーティリティーを使用します。

sh cfgSetQdmlRoot.sh -in=person.qdml -out=person.qdml -root=person

QJMLの作成

次に、Quickが使用するQJMLファイルを作成する必要があります。QJML (Quick Java Markup Language) は、JAXBや他のデータ・バインディング・インプリメンテーションにおけるバインディング・スキーマに対応します。QuickはQJMLを使用して、XMLファイル内の構成要素をJavaの構成要素に (また、Java構成要素をXML構成要素に) 変換します。

QJMLファイルはゼロから作成することもできますが、Quickには自動生成用のツールが備わっており、一般には最小限の変更をそのファイルに加えるだけですみます。そのため、このアプローチが推奨されています。これを行うには、cfgQdml2Qjml.bat またはcfgQdml2Qjml.sh スクリプトを使用し、新しく生成されたQDMLファイルを入力として提供します (QuickはQDMLを読み取り、マップする構成を判別します。このファイルが必要な理由がこれで分かりましたか?):

sh cfgQdml2Qjml.sh -in=person.qdml -out=person.qjml

これで、新しいファイルperson.qjml が得られました。

QuickはJava変数名に関していくつか問題のある仮定を行ってしまうので、前に述べたように、必ずこのファイルにいくつかの変更を加える必要があります。ファイルを開き、太字のコードを加えて、リスト7 のようにしてください。

リスト7. QJMLの編集
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<!DOCTYPE qjml SYSTEM "classpath:///qjml.dtd">
<qjml root="person">
    <bean tag="person">
        <targetClass>Person</targetClass>
        <elements>
            <item coin="firstName">
<property name="firstName"/>
            </item>
            <item coin="lastName">
<property name="lastName"/>
            </item>
            <item coin="address" repeating="True">
<property kind="list" name="addressList"/>
            </item>
            <item coin="phoneNumber" repeating="True">
<property kind="list" name="phoneNumberList"/>
            </item>
        </elements>
    </bean>
    <text tag="firstName"/>
    <text tag="lastName"/>
    <bean tag="address">
        <targetClass>Address</targetClass>
        <attributes>
            <item coin="address.type" optional="True" value="home">
<property initializer="home" name="type"/>
            </item>
        </attributes>
        <elements>
            <item coin="street">
<property name="street"/>
            </item>
            <item coin="city">
<property name="city"/>
            </item>
            <item coin="state">
<property name="state"/>
            </item>
            <item coin="zipCode">
<property name="zipCode"/>
            </item>
        </elements>
    </bean>
    <text label="address.type" tag="type">
        <enum value="home"/>
        <enum value="work"/>
        <enum value="other"/>
    </text>
    <text tag="street"/>
    <text tag="city"/>
    <text tag="state"/>
    <text tag="zipCode"/>
    <bean tag="phoneNumber">
        <targetClass>PhoneNumber</targetClass>
        <elements>
            <item coin="type">
<property name="type"/>
            </item>
            <item coin="number">
<property name="number"/>
            </item>
        </elements>
    </bean>
    <text tag="type"/>
    <text tag="number"/>
</qjml>

これらの変更内容は、元のファイルをリスト7 のファイルと比較すれば明らかです。いずれにも、ソース・コードで定義されているようにXMLプロパティーをJavaプロパティー名にマップすることが関係しています (リスト45、および6 を参照して思い返してください)。

QIMLの作成

これで、セットアップ作業はほとんど終了ですが、最後の1つのステップを実行する必要があります。Quickは、QIML (Quick Internal Markup Language) ファイルを使用したときにパフォーマンスが非常に良くなります。この名前が示すように、Quickはこの内部形式により、QJMLファイルの実行時の処理、コンパイル、および変換を回避します。以下の単純なステップを実行すれば、QJMLをQuickがより手軽に使用できる形式にすることができます。

sh cfgQjml2Qiml.sh -in=person.qjml -out=person.qiml

最後に、パフォーマンスをさらに向上させるため、このテキスト・ベースの形式を、コンパイル可能なJavaソース・ファイルにします (バイナリー・オブジェクトは、コンパイルされていないテキスト・ファイル形式よりも常に優れた処理を行えます)。

sh cfgQiml2Java.sh -in=person.qiml -out=PersonSchema.java 
-class=PersonSchema -key=person.qjml

ここで行われていることについてあまり心配する必要はありません。すべてはQuickに固有の処理で、ユーティリティーが適切に行います。その後、ここで作成されたソース (まだであれば、残りのJavaソースも) をコンパイルし、すべての情報がCLASSPATHに追加されていることを確かめる必要があります。これらのすべてのステップが完了したら、いくらかのデータ・バインディングを行えます。


データ・バインディング

すべてのセットアップ作業が完了したら、Quickフレームワークを使用してXML文書をJavaの構造に変換し、それから元に戻すことはささいなことです。ここでは、例が単純になっています。いったん基礎を理解できたら、データ・バインディングの使用法は無限にあります。それで、特定の使用法を想定する代わりに、サンプル・コードから必要な情報を使用して自由に作業できるようになっています。リスト8 を見た後で、主な概念について考えましょう。

リスト8. JavaでのQuickの使用
import java.util.Iterator;

// Quick imports
import com.jxml.quick.QDoc;
import com.jxml.quick.Quick;

public class PersonTest {

  public static void main(String[] args) {
    try {
      if (args.length != 2) {
        System.err.println("Usage: java PersonTest [input file] [output file]");
        return;
      }

      // Initialize Quick
      QDoc schema = PersonSchema.createSchema();

      // Convert input XML to Java
      QDoc doc = Quick.parse(schema, args[0]);

      // Get the result
      Person person = (Person)Quick.getRoot(doc);

      // Output block
      System.out.println(" --------------- Person ------------------ ");
      System.out.println(" First Name: " + person.getFirstName());
      System.out.println(" Last Name : " + person.getLastName());
      for (Iterator i = person.getAddressList().iterator(); i.hasNext(); ) {
        Address address = (Address)i.next();
        System.out.println(" Address (" + address.getType() + "):");
        System.out.println("   " + address.getStreet());
        System.out.println("   " + address.getCity() + ", " + address.getState() +
           " " + address.getZipCode());
      }
      for (Iterator i = person.getPhoneNumberList().iterator(); i.hasNext(); ) {
        PhoneNumber number = (PhoneNumber)i.next();
        System.out.println(" Phone Number (" + number.getType() + "):");
        System.out.println("   " + number.getNumber());
      }

      // Add a new address
      Address address = 
        new Address("work", "357 West Magnolia Lane", "Waco", "TX", "76710");
      person.getAddressList().add(address);

      // Change a phone number
      PhoneNumber number = (PhoneNumber)person.getPhoneNumberList().get(1);
      number.setNumber("2547176547");

      // Write out modified XML
      Quick.express(doc, args[1]);
    } catch (Exception e) {
       e.printStackTrace();
    }
  }
}

このコードでQuickを扱っている部分はたったの4行ほどです。まず、QIML (Javaバイトコードとしてコンパイルされている) をロードして、どのXMLエレメントおよび属性がどのJavaクラスおよびプロパティーになるかをQuickに知らせなければなりません。これを行うには、生成されたPersonSchema クラスのcreateSchema() メソッド (静的メソッド) を使用します。このスキーマがロードされると、スキーマと入力ファイルがQuick.parse() メソッドに渡され、変換が行われます。あとは、ここで生成されるQDoc のルート・エレメントを取り出し、他のJavaオブジェクトのように処理するだけです。コードの最後の部分ではQuickが再び登場し、express() メソッドによって変更されたバージョンのXMLが出力されます。とても簡単でしょう?

注:絶対に、入力と出力で同じファイル名を指定しないでください。元のデータが上書きされ、あらゆる種類の不測の事態が生じる可能性があります。


結論

この記事で本当に魅力的な機能を見いだせたとしたら、望ましいことです。まず、一般的なデータ・バインディングにより、プログラミング作業を大幅に単純化できます。これは特に、データを何らかの静的ストレージ (この記事のように、ファイルなど) に保存しなければならない場合に当てはまります。さらに、Quickでは、独自のプロジェクトで高速かつ単純にこれを行えます。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, Java technology
ArticleID=241326
ArticleTitle=QuickによるJavaオブジェクトとXMLの変換
publish-date=08012002