今週からJAXBのバインディングをカスタマイズする方法について説明していきます。
とはいうものの,JAXBのバインディングはほとんどの場合,カスタマイズする必要がありません。しかし,たとえばスキーマからJavaのクラスを生成する時,Javadocを記述したいということがあるかもしれません。また,名前の衝突が発生してしまうこともあります。
このような場合にバインディングをカスタマイズし,問題を解消するようにします。
バインディングにはスキーマからJavaと,Javaからスキーマの両方向がありますが,それぞれカスタマイズを行なうことができます。しかし,Javaからスキーマへのバインディングはアノテーションが使用できるため,カスタマイズを行なうことは少ないはずです。
そこで,今週と来週でスキーマからJavaへのバインディングにおけるカスタマイズ,再来週にJavaからスキーマへのバインディングにおけるカスタマイズについて説明します。
インラインと外部ファイル
スキーマからJavaへのバインディングをカスタマイズするには2つの手法があります。
- XML Schemaファイルにカスタマイズのための情報を埋め込む(インラインカスタマイズ)
- 外部のファイルにカスタマイズのための情報を記述する(外部ファイルによるカスタマイズ)
インラインカスタマイズはカスタマイズしたい要素に直接記述できるので,どのようなカスタマイズを行なうかが一目瞭然です。しかし,スキーマの定義の可読性は下がってしまいます。
外部ファイルに記述する場合,カスタマイズを行なう対象をXPathで指定しなくてはならず,やや煩雑です。しかし,スキーマファイルを第三者が所有していて,変更できない場合などに重宝します。また,スキーマは変更しないため,スキーマ自体の可読性は維持されます。
このように2つの手法の特徴は異なるので,用途に応じて使い分けをするようにしましょう。
それぞれの手法は記述方法も異なります。
インラインカスタマイズは,XML Schemaで定義されているカスタマイズ用の<annotation>要素と<appinfo>要素を組み合わせて記述します。
<xs:annotation> <xs:appinfo> ここにカスタマイズのための情報を記述する </xs:appinfo> </xs:annotation>
xsはXML Schemaの名前空間を表すプリフィックスです。
<annotation>要素は<schema>要素や,<complexType>要素,<element>要素などの子要素として記述することができます。したがって,カスタマイズを行ないたい部分に<aoontation>要素を埋め込むことができます。
一方,外部ファイルに記述する方法は<bindings>要素を使用して記述します。
<jxb:bindings schemaLocation = "スキーマファイルのURI"> <jxb:bindings node = "XPathによるノードの指定"> ここにカスタマイズのための情報を記述する <jxb:bindings> </jxb:bindings>
jxbはJAXBの名前空間を表すプリフィックスです。
<bindings>要素は入れ子にして使用することができます。一番外側の<bindings>要素では,schemaLocation属性でXML Schemaファイルを指定します。
<bindings>要素は,カスタマイズの対象となるノードを示すためにnode属性を使用します。node属性はXPathを用いて記述します。
このXPath式は,多段で指定することが可能です。つまり,<bindings>要素を入れ子にし,複数のXPath式でノードを指定することができるのです。このため,XPath式の記述を簡略することができます。
使い方の例として,名前空間とは異なるパッケージでクラスを生成してみましょう。また,スキーマとプロパティで名前を変更することも行なってみます。
まず,対象となるXML Schemaを次に示します。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <xs:schema version="1.0" targetNamespace="http://www.javainthebox.net/music" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="album"> <xs:complexType> <xs:attribute name="title" type="xs:string"/> <xs:attribute name="credit" type="xs:string"/> </xs:complexType> </xs:element> </xs:schema>
このまま,xjcでJavaのクラスを生成させると,net.javainthebox.musicパッケージにクラスを生成します。それをnet.javainthebox.sampleに変更してみます。また,credit属性をartistプロパティに対応づけてみます。
はじめにインラインカスタマイズを使用したカスタマイズです。
インラインカスタマイズでは,前述したようにスキーマファイルに直接記述します。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <xs:schema version="1.0" targetNamespace="http://www.javainthebox.net/music" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" jxb:version="2.1"> <xs:annotation> <xs:appinfo> <jxb:schemaBindings> <jxb:package name="net.javainthebox.sample"/> </jxb:schemaBindings> </xs:appinfo> </xs:annotation> <xs:element name="album"> <xs:complexType> <xs:attribute name="title" type="xs:string"/> <xs:attribute name="credit" type="xs:string"> <xs:annotation> <xs:appinfo> <jxb:property name="artist"/> </xs:appinfo> </xs:annotation> </xs:attribute> </xs:complexType> </xs:element> </xs:schema>
青および赤で示した部分が追加した部分です。
JAXBで定義された要素を使用するので,名前空間を設定する必要があります。また,JAXBのバージョンも一緒に<schema>要素の属性として記述します。
赤字で示した部分が実際にカスタマイズを行なっている部分です。<schema>要素に対するカスタマイズと,<attribute>要素に対するカスタマイズを行なっています。
これらの要素の具体的な解説は来週行ないます。
さて,このスキーマファイルからJavaのクラスを生成してみましょう。インラインカスタマイズの場合,通常のスキーマファイルからのバインディングとまったく同じ方法でバインディングを行なうことができます。
C:\jaxb>xjc album_inline.xsd parsing a schema... compiling a schema... net\javainthebox\sample\Album.java net\javainthebox\sample\ObjectFactory.java net\javainthebox\sample\package-info.java
カスタマイズした結果,パッケージがnet.javainthebox.sampleになったことがわかります。Albumクラスは次のようになりました(コメントを省略してあります)。
package net.javainthebox.sample; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "") @XmlRootElement(name = "album") public class Album { @XmlAttribute protected String title; @XmlAttribute(name = "credit") protected String artist; public String getTitle() { return title; } public void setTitle(String value) { this.title = value; } public String getArtist() { return artist; } public void setArtist(String value) { this.artist = value; } }
credit属性がartistプロパティに変更されています。また,変更されたことが分かるように,@XmlAttributeアノテーションで名前が指定されています。
このように,カスタマイズが行なわれたことが分かります。
次に外部ファイルを用いてカスタマイズを行なってみます。
カスタマイズ用の外部ファイルは通常拡張子をxjbにします。binding.xjbファイルを次に示します。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <jxb:bindings version="1.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <jxb:bindings schemaLocation="album.xsd" node="/xs:schema"> <jxb:schemaBindings> <jxb:package name="net.javainthebox.sample"/> </jxb:schemaBindings> <jxb:bindings node="//xs:attribute[@name='credit']"> <jxb:property name="artist"/> </jxb:bindings> </jxb:bindings> </jxb:bindings>
青字の部分がノードを指定している部分です。実際のカスタマイズは赤字で示しました。この部分は,先ほどのインラインカスタマイズとまったく同じです。
外部ファイルを使用する場合,xjcの-bオプションで指定します。複数の外部ファイルがある場合は,xjc [schema] -b [ext file1] -b [ext file2] のように続けて指定します。
C:\jaxb>xjc -b binding.xjb album.xsd parsing a schema... compiling a schema... net\javainthebox\sample\Album.java net\javainthebox\sample\ObjectFactory.java net\javainthebox\sample\package-info.java
インラインカスタマイズの時と同様に,net.javainthebox.sampleパッケージにクラスを生成しました。生成したクラスも先ほどと同じです。
このように,バインディングをカスタマイズできることが分かりました。では,具体的にどのようなカスタマイズができるのでしょうか。それは来週のお楽しみということにしましょう。