Oracle® Fusion Middleware

Oracle TopLinkによるJAXBアプリケーションの開発

12c (12.2.1.2.0)

E82672-01(原本部品番号:E79109-01)

2016年10月

Oracle Fusion Middleware Oracle TopLinkによるJAXBアプリケーションの開発, 12c (12.2.1.2.0)

E82672-01

Copyright © 1997, 2016 Oracle and/or its affiliates. All rights reserved.

このソフトウェアおよび関連ドキュメントの使用と開示は、ライセンス契約の制約条件に従うものとし、知的財産に関する法律により保護されています。ライセンス契約で明示的に許諾されている場合もしくは法律によって認められている場合を除き、形式、手段に関係なく、いかなる部分も使用、複写、複製、翻訳、放送、修正、ライセンス供与、送信、配布、発表、実行、公開または表示することはできません。このソフトウェアのリバース・エンジニアリング、逆アセンブル、逆コンパイルは互換性のために法律によって規定されている場合を除き、禁止されています。

ここに記載された情報は予告なしに変更される場合があります。また、誤りが無いことの保証はいたしかねます。誤りを見つけた場合は、オラクル社までご連絡ください。

このソフトウェアまたは関連ドキュメントを、米国政府機関もしくは米国政府機関に代わってこのソフトウェアまたは関連ドキュメントをライセンスされた者に提供する場合は、次の通知が適用されます。

U.S. GOVERNMENT END USERS: Oracle programs, including any operating system, integrated software, any programs installed on the hardware, and/or documentation, delivered to U.S. Government end users are "commercial computer software" pursuant to the applicable Federal Acquisition Regulation and agency-specific supplemental regulations. As such, use, duplication, disclosure, modification, and adaptation of the programs, including any operating system, integrated software, any programs installed on the hardware, and/or documentation, shall be subject to license terms and license restrictions applicable to the programs. No other rights are granted to the U.S. Government.

このソフトウェアまたはハードウェアは様々な情報管理アプリケーションでの一般的な使用のために開発されたものです。このソフトウェアまたはハードウェアは、危険が伴うアプリケーション(人的傷害を発生させる可能性があるアプリケーションを含む)への用途を目的として開発されていません。このソフトウェアもしくはハードウェアを危険が伴うアプリケーションで使用する際、安全に使用するために、適切な安全装置、バックアップ、冗長性(redundancy)、その他の対策を講じることは使用者の責任となります。このソフトウェアまたはハードウェアを危険が伴うアプリケーションで使用したことに起因して損害が発生しても、オラクル社およびその関連会社は一切の責任を負いかねます。

OracleおよびJavaはOracle Corporationおよびその関連企業の登録商標です。その他の名称は、それぞれの所有者の商標または登録商標です。

Intel、Intel Xeonは、Intel Corporationの商標または登録商標です。すべてのSPARCの商標はライセンスをもとに使用し、SPARC International, Inc.の商標または登録商標です。AMD、Opteron、AMDロゴ、AMD Opteronロゴは、Advanced Micro Devices, Inc.の商標または登録商標です。UNIXは、The Open Groupの登録商標です。

このソフトウェアまたはハードウェア、そしてドキュメントは、第三者のコンテンツ、製品、サービスへのアクセス、あるいはそれらに関する情報を提供することがあります。適用されるお客様とOracle Corporationとの間の契約に別段の定めがある場合を除いて、Oracle Corporationおよびその関連会社は、第三者のコンテンツ、製品、サービスに関して一切の責任を負わず、いかなる保証もいたしません。適用されるお客様とOracle Corporationとの間の契約に定めがある場合を除いて、Oracle Corporationおよびその関連会社は、第三者のコンテンツ、製品、サービスへのアクセスまたは使用によって損失、費用、あるいは損害が発生しても一切の責任を負いかねます。

このドキュメントの情報は、情報共有の目的のみに使用され、顧客諮問委員会の会員の立場で考慮されるか、ベータ・トライアル版契約のみに準じる必要があります。資料、規約または機能性を配信する義務はなく、購買決定に依存すべきではないものとします。このドキュメントに記載されている開発、リリース、およびすべての機能または機能性の時期は、オラクル社の独自の判断に従うものとします。

このドキュメントは、ソフトウェアまたは印刷物などの形式にかかわらず、オラクル社の独占的な財産である機密情報を含みます。この機密資料へのアクセスと使用は、オラクル社との間で締結され遵守に同意したOracle Master Agreement、Oracle License and Services Agreement、Oracle PartnerNetwork Agreement、オラクル販売契約、あるいはその他のライセンス契約の条件にしたがうものとします。オラクル社の文書による事前承諾なしに、このドキュメントおよびここに含まれる情報を開示、コピー、再生、またはオラクル社外部の他者に配布することはできません。このドキュメントは使用許諾契約の一部ではなく、オラクル社の子会社またはアフィリエイトとの契約上の合意に組み込まれません。

目次

例一覧

図一覧

タイトルおよび著作権情報

はじめに

- 対象読者

- ドキュメントのアクセシビリティについて

- 関連ドキュメント

- 表記規則

1 概要

- 1.1 EclipseLink MOXyについて

- 1.2 object-xmlのインピーダンス・ミスマッチの解決

- 1.3 このドキュメントについて

- 1.3.1 その他のリソース

2 EclipseLink MOXyランタイム

- 2.1 EclipseLinkランタイムの指定

- 2.2 ブートストラップ

- 2.2.1 JAXBContext APIの使用

- 2.2.2 クラスからのブートストラップ

- 2.2.3 コンテキスト・パスからのブートストラップ

- 2.2.3.1 jaxb.indexファイルの使用

- 2.2.3.2 ObjectFactoryの使用

- 2.2.3.3 MetadataSourceの使用

- 2.2.4 EclipseLink XMLバインディングからのブートストラップ

- 2.2.5 注釈付きクラスとXMLバインディングの組合せ

- 2.3 XMLバインディングの使用

- 2.3.1 XMLバインディング形式の理解

- 2.3.2 XMLバインディングを使用したブートストラップ

- 2.3.3 XMLバインディングの注釈付き使用

- 2.3.4 複数のバインディング・ドキュメントの使用

- 2.3.5 オーバーライド・ルールの理解

- 2.3.6 完全なメタデータの使用

- 2.3.7 仮想マッピングの使用

- 2.4 MetadataSourceの使用

- 2.4.1 MetadataSourceの実装

- 2.4.2 XmlBindingsオブジェクトの使用

- 2.4.3 MetadataSourceの指定

- 2.4.4 MetadataSourceの例

- 2.4.5 プログラムによるXmlBindingsの構築

- 2.5 XMLスキーマの生成

- 2.6 XMLスキーマに対する検証

- 2.6.1 ValidationEventHandlerの使用

- 2.6.2 検証の有効化

- 2.6.2.1 入力(input.xmlファイル)

- 2.6.2.2 出力

- 2.7 イベントの理解

- 2.7.1 JAXBでマップされたオブジェクトへのイベント・リスナー・メソッドの追加

- 2.7.2 マーシャラおよびアンマーシャラに対するリスナーの登録

- 2.8 XPathによるオブジェクトの問合せ

- 2.9 既存の文書へのバインド

3 タイプ・レベルのマッピング

- 3.1 デフォルトのルート要素の定義

- 3.1.1 デフォルトのルート要素のカスタマイズ

- 3.1.2 EclipseLinkがデフォルトのルート要素をどのように使用するかの理解

- 3.2 ネームスペース情報の設定

- 3.2.1 パッケージ・レベルでの修飾

- 3.2.2 タイプ・レベルでの修飾

- 3.2.3 フィールド/プロパティ・レベルでの修飾

- 3.3 継承の指定

- 3.3.1 xsi:typeの使用

- 3.3.2 置換グループの使用

- 3.3.3 @XmlDiscriminatorNode/@XmlDiscriminatorValueの使用

4 単純な値のマッピング

- 4.1 単純な値のマッピング

- 4.1.1 属性へのマッピング

- 4.1.2 テキスト・ノードへのマッピング

- 4.1.2.1 単純な順序でのテキスト・ノードへのマッピング

- 4.1.2.2 サブ要素でのテキスト・ノードへのマッピング

- 4.1.2.3 位置によるテキスト・ノードへのマッピング

- 4.1.2.4 単純なテキスト・ノードへのマッピング

- 4.1.3 指定のスキーマ・タイプへのマッピング

- 4.1.3.1 Javaタイプ・アダプタの使用

- 4.1.4 シンプル・タイプ・トランスレータによるマッピング

- 4.2 単純な値のコレクションのマッピング

- 4.2.1 テキスト・ノードへのマッピング

- 4.2.2 グループ化要素によるテキスト・ノードへのマッピング

- 4.2.3 リスト要素へのマッピング

- 4.2.4 XmlAttributesまたはXmlValuesのコレクションのマッピング

- 4.3 単一のプロパティに対する複数のマッピング

- 4.3.1 例

- 4.3.2 XML出力

- 4.4 列挙のマッピング

- 4.4.1 定数名を使用した列挙のマッピング

- 4.4.2 カスタムXML値への列挙のマッピング

5 特殊なスキーマ・タイプのマッピング

- 5.1 日付および時刻のマッピング

- 5.1.1 生成されたモデルの理解

- 5.1.2 異なるDate(またはCalendar)プロパティの使用

- 5.2 unionフィールドへのマッピング

- 5.2.1 変換順の理解

- 5.2.2 変換クラスのカスタマイズ

- 5.3 バイナリ型

- 5.3.1 バイナリのBase64形式およびHex形式の指定

- 5.3.2 byte[]とByte[]の比較の理解

- 5.3.3 SOAP添付ファイルの使用

- 5.3.3.1 @XmlInlineBinaryDataの使用

- 5.3.3.2 @XmlMimeTypeの使用

6 私有のリレーションシップ

- 6.1 私有の1対1リレーションシップのマッピング

- 6.1.1 要素へのマッピング

- 6.1.2 EclipseLinkの@XmlPath注釈の使用

- 6.2 私有の1対多リレーションシップのマッピング

- 6.2.1 要素へのマッピング

- 6.2.2 @XmlElementWrapper注釈を使用したグループ化要素

7 共有参照のリレーションシップのマッピング

- 7.1 キーと外部キーの理解

- 7.2 単一キーのリレーションシップのマッピング

- 7.2.1 @XmlListの使用

- 7.3 組込みキー・クラスの使用

- 7.4 コンポジット・キーのリレーションシップのマッピング

- 7.5 双方向リレーションシップのマッピング

8 拡張概念

- 8.1 メタデータのリフレッシュ

- 8.2 XML名変換のカスタマイズ

- 8.2.1 XMLNameTransformerの使用

- 8.2.2 サンプル・モデル

- 8.2.3 ネーミング・アルゴリズムの指定

- 8.2.4 XML出力

- 8.3 仮想アクセス・メソッドの使用

- 8.3.1 仮想アクセス・メソッドの構成

- 8.3.2 例

- 8.3.3 XmlAccessType.FIELDおよびXmlTransientの使用

- 8.3.4 オプション

- 8.3.4.1 代替のアクセッサ・メソッドの指定

- 8.3.4.2 スキーマ生成オプションの指定

- 8.4 拡張可能MOXyの使用

- 8.4.1 @XmlVirtualAccessMethods注釈の使用

- 8.4.2 テナントの作成1

- 8.4.3 テナントの作成2

- 8.5 XPath述語を使用したマッピング

- 8.5.1 XPath述語によるマッピング

- 8.5.2 位置に基づくマッピング

- 8.5.3 属性値に基づくマッピング

- 8.5.4 セルフ・マッピングの作成

- 8.6 XmlAdapterの使用

- 8.6.1 java.util.Currencyの使用

- 8.6.2 java.awt.Pointの使用

- 8.6.3 パッケージ・レベル・アダプタの指定

- 8.6.4 クラス・レベルの@XmlJavaTypeAdaptersの指定

- 8.7 XML変換の使用

- 8.7.1 AttributeTransformerの使用

- 8.7.2 FieldTransformerの使用

- 8.8 XMLスキーマからのJavaクラスの生成

- 8.8.1 JAXBコンパイラの実行

- 8.9 生成されたマッピングのカスタマイズ

9 動的JAXBの使用

- 9.1 静的エンティティと動的エンティティの理解

- 9.1.1 静的MOXyの使用

- 9.1.2 動的MOXyの使用

- 9.1.2.1 動的エンティティの使用

- 9.2 EclipseLinkランタイムの指定

- 9.2.1 DynamicJAXBContextのインスタンス化

- 9.3 XMLスキーマ(XSD)からのブートストラップ

- 9.3.1 他のスキーマ/EntityResolverのインポート

- 9.3.2 XJC外部バインディング・カスタマイズ・ファイルで生成されたマッピングのカスタマイズ

- 9.4 EclipseLinkメタデータ(OXM)からのブートストラップ

- 9.4.1 例

10 JSONドキュメントの使用

- 10.1 JSONドキュメントの理解

- 10.2 JSONドキュメントのマーシャリングおよびアンマーシャリング

- 10.3 JSONバインディングの指定

- 10.3.1 JSONデータ型の指定

- 10.3.2 属性のサポート

- 10.3.3 ルート要素なしのサポート

- 10.3.4 ネームスペースの使用

- 10.3.5 コレクションの使用

- 10.3.6 ルート・レベル・コレクションのマッピング

- 10.3.7 XML値のラップ

例一覧

- 2-1 JAXBContextのメソッド

- 2-2 jaxb.indexファイルのサンプル

- 2-3 ObjectFactoryのサンプル

- 2-4 MetadataSourceのサンプル

- 2-5 EclipseLinkバインディング・ドキュメントの使用

- 2-6 Javaクラスのサンプル

- 2-7 XMLバインディング・ドキュメントの使用

- 2-8 アプリケーション・コードのサンプル

- 2-9 バインディング・ドキュメントの例

- 2-10 JAXBドメイン・クラスとXMLのサンプル

- 2-11 カスタマイズされたXMLバインディング

- 2-12 バインディングの提供

- 2-13 XMLバインディングのリストの使用

- 2-14 複数のパッケージでの1つのマップの使用

- 2-15 Customerクラスのサンプル

- 2-16 XMLバインディング

- 2-17 XML表現

- 2-18 XMLバインディング(xml-accessor-type="NONE"と指定されている)

- 2-19 XML表現

- 2-20 XMlBindingsメソッドの実装

- 2-21 XmlBindingsオブジェクトのサンプル

- 2-22 プロパティ・マップへのMetadataSourceの追加

- 2-23 XmlBindingsオブジェクトのサンプル

- 2-24 XmlBindingsオブジェクトのサンプル

- 2-25 XMLスキーマのサンプル

- 2-26 Customerクラスのサンプル

- 2-27 ValidationEventHandlerのサンプル

- 2-28 Javaコードのサンプル

- 2-29 イベント・リスナーのサンプル

- 2-30 リスナー・クラスを使用したロギング

- 2-31 バインダの使用

- 3-1 XMLスキーマのサンプル

- 3-2 @XmlRootElement注釈の使用

- 3-3 デフォルトのルート要素の指定

- 3-4 注釈の使用

- 3-5 OXMメタデータの使用

- 3-6 注釈の使用

- 3-7 OXMメタデータの使用

- 3-8 注釈の使用

- 3-9 XMLバインディング・ファイルの使用

- 3-10 ネームスペースのオーバーライド

- 3-11 バインディング・ファイルのサンプル

- 3-12 Javaクラスのサンプル

- 3-13 @XmlRootElement注釈の使用

- 3-14 @XmlDiscriminatorNode注釈および@XmlDiscriminatorValue注釈の使用

- 4-1 XMLスキーマのサンプル

- 4-2 @XmlAttribute注釈の使用

- 4-3 マッピングの定義

- 4-4 XMLスキーマのサンプル

- 4-5 @XmlElement注釈の使用

- 4-6 name属性の使用

- 4-7 XMLスキーマのサンプル

- 4-8 @XmlPath注釈の使用

- 4-9 xml-path属性の使用

- 4-10 XMLスキーマのサンプル

- 4-11 @XmlPath注釈の使用

- 4-12 マッピングの定義

- 4-13 XMLスキーマのサンプル

- 4-14 @XmlValue注釈の使用

- 4-15 マッピングの定義

- 4-16 XMLスキーマのサンプル

- 4-17 @XmlSchemaType注釈の使用

- 4-18 マッピングの定義

- 4-19 XMLスキーマのサンプル

- 4-20 @XmlSchemaType注釈および@XmlJavaTypeAdapter注釈の使用

- 4-21 マッピングの定義

- 4-22 XMLスキーマのサンプル

- 4-23 マッピングのサンプル

- 4-24 XMLマッピングのサンプル

- 4-25 XMLスキーマのサンプル

- 4-26 @XmlElement注釈の使用

- 4-27 XMLマッピングのサンプル

- 4-28 XMLスキーマのサンプル

- 4-29 @XmlElementWrapper注釈の使用

- 4-30 XMLマッピングのサンプル

- 4-31 XMLスキーマのサンプル

- 4-32 @XmlList注釈の使用

- 4-33 XMLマッピングのサンプル

- 4-34 Javaの注釈

- 4-35 EclipseLink OXMメタデータ

- 4-36 XML文書の例

- 4-37 Javaクラスのサンプル

- 4-38 XMLスキーマのサンプル

- 4-39 結果となるXML

- 4-40 XMLスキーマのサンプル

- 4-41 @XmlEnum注釈の使用

- 4-42 XMLマッピングのサンプル

- 4-43 XMLスキーマのサンプル

- 4-44 @XmlEnumValue注釈の使用

- 4-45 XMLマッピングのサンプル

- 5-1 XMLスキーマのサンプル

- 5-2 Customerクラスのサンプル

- 5-3 java.util.Dateの使用

- 5-4 XMLスキーマの結合

- 5-5 EclipseLinkのカスタマイザの使用

- 5-6 unionフィールドのマッピング

- 5-7 注釈

- 5-8 EclipseLink OXM

- 5-9 出力

- 5-10 byte[]とByte[]の使用

- 5-11 出力

- 5-12 Javaクラスのサンプル

- 5-13 結果となるXML

- 5-14 @XmlInlineBinaryData注釈の使用

- 5-15 @XmlMimeType注釈の使用

- 6-1 XMLマッピングのサンプル

- 6-2 XMLスキーマのサンプル

- 6-3 @XmlElement注釈の使用

- 6-4 XMLマッピングのサンプル

- 6-5 @XmlPath注釈の使用

- 6-6 EclipseLinkのXMLバインディングの使用

- 6-7 @XmlPath注釈の使用

- 6-8 XMLマッピングのサンプル

- 6-9 @XmlElement注釈の使用

- 6-10 XMLマッピングのサンプル

- 6-11 @XmlElementWrapper注釈の使用

- 7-1 @XmlIDおよび@XmlIDREF注釈の使用

- 7-2 XMLマッピングのサンプル

- 7-3 @XmlList注釈の使用

- 7-4 組込みIDのサンプル

- 7-5 @XmlCustomizer注釈の使用

- 7-6 XPathの変更

- 7-7 @XmlTransient注釈の使用

- 7-8 @XmlKeyおよび@XmlJoinNodes注釈の使用

- 7-9 XMLマッピングのサンプル

- 7-10 @XMlInverseReference注釈の使用

- 7-11 XMLマッピングのサンプル

- 8-1 メタデータのリフレッシュ

- 8-2 XMLNameTransformerの使用

- 8-3 Customer

- 8-4 Address.java

- 8-5 Java注釈の使用

- 8-6 OXMメタデータの使用

- 8-7 virtualprops-oxm.xmlファイル

- 8-8 Map属性のマーク付け

- 8-9 代替のアクセッサ・メソッドの使用

- 8-10 xml-virtual-access-methods要素の使用

- 8-11 個々のノードとしてのマッピング

- 8-12 元のCustomerスキーマ

- 8-13 生成されたスキーマ(middle-initialとphone-numberの追加後)

- 8-14 <any>要素の使用

- 8-15 生成されたスキーマ

- 8-16 ExtensibleBaseのサンプル

- 8-17 Customer

- 8-18 Address

- 8-19 PhoneNumber

- 8-20 binding-tenant1.xml

- 8-21 出力

- 8-22 binding-tenant2.xml

- 8-23 出力

- 8-24 JavaコードとXMLスキーマのサンプル

- 8-25 JavaコードとXMLスキーマのサンプル

- 8-26 @XmlType(propOrder)注釈の使用

- 8-27 prop-order属性の使用

- 8-28 結果となるXML

- 8-29 XMLスキーマのサンプル

- 8-30 マッピングのサンプル

- 8-31 セルフ・マッピングの例

- 8-32 XmlAdapterクラスの概要

- 8-33 ドメイン・クラスのサンプル

- 8-34 アダプタの使用

- 8-35 @XmlJavaTypeAdapter注釈の使用

- 8-36 java.awt.Pointの使用

- 8-37 @XmlJavaTypeAdapter注釈の使用

- 8-38 マッピングの例

- 8-39 AttributeTransfomerのサンプル

- 8-40 1番目の書込みトランスフォーマ

- 8-41 2番目の書込みトランスフォーマ

- 8-42 eclipselink-oxm.xmlファイルのサンプル

- 9-1 Javaクラスのサンプル

- 9-2 マーシャリングおよびアンマーシャリングの例

- 9-3 マーシャリングおよびアンマーシャリングの例

- 9-4 DynamicJAXBContextの作成

- 9-5 XMLスキーマのサンプル

- 9-6 アプリケーション・コードのサンプル

- 9-7 XMLスキーマのサンプル

- 9-8 アプリケーション・コードのサンプル

- 9-9 custom1.xjbファイル

- 9-10 ブートストラップの例

- 9-11 DynamicJAXBContextの作成

- 9-12 XMLスキーマのサンプル

- 9-13 アプリケーション・コードのサンプル

- 10-1 マーシャリングおよびアンマーシャリング

- 10-2 マップの使用

- 10-3 MarshallerPropertiesおよびUnarshallerPropertiesの使用

- 10-4 基本的なJSONバインディングの使用

- 10-5 外部バインディングの使用

- 10-6 JSONを使用したJAXBContextのブートストラップ

- 10-7 JSONデータ型の使用

- 10-8 接頭辞の使用

- 10-9 Mapへの接頭辞の設定

- 10-10 ルート要素のないドキュメントのマーシャリング

- 10-11 ルート要素のないドキュメントのアンマーシャリング

- 10-12 ネームスペースの使用

- 10-13

- 10-14 @XmlAttributesの使用

- 10-15 値ラッパーの使用

- 10-16

- 10-17 マップの使用

図一覧

- 1-1 JAXBのサンプル・アーキテクチャ

- 3-1 マップされたクラスのサンプル

- 4-1 属性へのXMLダイレクト・マッピング

- 4-2 個々のテキスト・ノードへのXMLダイレクト・マッピング

- 4-3 サブノードでのテキスト要素へのXMLダイレクト・マッピング

- 4-4 テキスト・ノードへのXMLダイレクト・マッピング

- 4-5 単純なテキスト・ノードへのXMLダイレクト・マッピング

- 4-6 特定のスキーマ・タイプへのXMLダイレクト・マッピング

- 4-7 別々のバイナリ型の異なるテキスト・ノードへのXMLダイレクト・マッピング

- 4-8 Javaクラスのサンプル

- 4-9 シンプル・タイプ・トランスレータによるXMLダイレクト・マッピング

- 4-10 テキスト・ノードへのXMLダイレクト・コレクション・マッピング

- 4-11 グループ化要素によるテキスト・ノードへのXMLダイレクト・コレクション・マッピング

- 4-12 listフィールドへのXMLダイレクト・マッピング

- 5-1 unionフィールドへのマッピング

- 5-2 XML文書内にあるunionフィールドへのマッピング

- 5-3 unionフィールドへのマッピング

- 6-1 1対1リレーションシップ

- 6-2 1対多リレーションシップ

はじめに

Oracle TopLinkは、Java Architecture for XML Binding (JAXB)実装のサポートに加えて、固有の注釈(TopLink拡張機能)を提供します。TopLink拡張を使用して、JPAエンティティ内でTopLinkの拡張機能を活用することができます。

TopLinkでは、デフォルトのJAXB実装としてEclipseLink MOXyが使用され(含まれ)ます。

対象読者

このドキュメントは、Java Architecture for XML Binding (JAXB)によってTopLinkを使用してアプリケーションを開発する開発者を対象としています。このドキュメントでは、関連する一般的なタスクの詳細は説明せず、TopLinkの機能を中心に説明します。

開発者は、次の概念とプログラミング技法を十分に理解している必要があります:

- Java SEおよびJava EE。

- Java Architecture for XML Binding (JAXB)の仕様(http://jcp.org/aboutJava/communityprocess/mrel/jsr222/index.html)

- Java Persistenceの仕様(http://jcp.org/en/jsr/detail?id=317)

- EclipseLink (http://www.eclipse.org/eclipselink)

ドキュメントのアクセシビリティについて

Oracleのアクセシビリティについての詳細情報は、Oracle Accessibility ProgramのWebサイト(http://www.oracle.com/pls/topic/lookup?ctx=acc&id=docacc)を参照してください。

Oracle Supportへのアクセス

サポートをご購入のOracleのお客様は、My Oracle Supportにアクセスして電子サポートを受けることができます。詳細は、http://www.oracle.com/pls/topic/lookup?ctx=accid=infoまたはhttp://www.oracle.com/pls/topic/lookup?ctx=accid=trs (聴覚障害者向け)を参照してください。

関連ドキュメント

詳細は、次のドキュメントを参照してください。

- Oracle TopLinkの理解

- Oracle TopLinkソリューション・ガイド

- Oracle Fusion Middleware EclipseLink Java APIリファレンス

- http://wiki.eclipse.org/EclipseLink/UserGuideのEclipseLink Documentation Center

- Oracle TopLinkリリース・ノート

表記規則

このマニュアルでは次の表記規則を使用します。

規則

意味

太字

太字は、操作に関連するGraphical User Interface要素、または本文中で定義されている用語および用語集に記載されている用語を示します。

イタリック体

イタリックは、ユーザーが特定の値を指定するプレースホルダ変数を示します。

固定幅フォント

固定幅フォントは、段落内のコマンド、URL、サンプル内のコード、画面に表示されるテキスト、または入力するテキストを示します。

1 概要

JAXB (Java Architecture for XML Binding - JSR 222)は、JavaのXMLバインディングの標準です。JAXBではXMLスキーマのコンセプトが100%カバーされており、EclipseLinkではJAXBの実装が多くの拡張機能とともに提供されています。JAXB仕様の詳細は、http://jcp.org/en/jsr/detail?id=222を参照してください。

Oracle TopLinkには、EclipseLink、およびそのJAXBプロバイダであるEclipseLink MOXyが含まれています。

開発者は、EclipseLink MOXyコンポーネントを使用することによって、XMLスキーマおよびJSONドキュメントにJavaクラスを効率的にバインドできます。MOXyではJAXBが実装されるので、開発者は、注釈でマッピング情報を指定でき、マッピングをXML形式およびJSON形式で保存できます。

この章の内容は、次のとおりです。

- EclipseLink MOXyについて

- object-xmlのインピーダンス・ミスマッチの解決

1.1 EclipseLink MOXyについて

EclipseLink MOXyをJAXBプロバイダとして使用する際には、既存のオブジェクト・モデルをXMLまたはJSONに変換するためのメタデータは不要です。(注釈、XMLまたはJSONを使用して)メタデータを提供できるのは、XMLまたはJSONの表現の微調整が必要なときのみです。

EclipseLink MOXyには、Javaクラス・モデルでスキーマをミラーリングしなくても、複雑なXML構造を処理できる高度なマッピングが多数含まれています。

図1-1 JAXBのサンプル・アーキテクチャ

1.2 object-xmlのインピーダンス・ミスマッチの解決

XMLはデータの交換に広く使用される形式ですが、多くのアプリケーションでは、プログラム表現として、XMLよりオブジェクトのほうが優先的に使用されます。オブジェクト・レベルで操作するには、XMLデータをオブジェクト形式に変換する必要があります。XMLとオブジェクトのミスマッチは、オブジェクトとobject-xmlのインピーダンス・ミスマッチと呼ばれます。

JAXBでは、ドメインのようなオブジェクトを使用することによって、XMLデータと対話できます。DOMオブジェクトとは異なり、JAXBコンテンツ・モデルは、XMLスキーマに基づいてXML文書を洞察します。たとえば、顧客情報を含むXML文書がXMLスキーマで定義されている場合は、コンテンツ・モデルには、Customer、Address、PhoneNumberなどのオブジェクトが含まれます。XMLスキーマ内の各タイプには、対応するJavaクラスが割り当てられます。

(TopLinkに含まれている)EclipseLink MOXyを使用することによって、XMLスキーマまたはJSONドキュメントにJavaクラスを効率的にバインドできます。MOXyでは、注釈でマッピング情報を指定し、マッピングをXMLまたはJSONで保存できるようにすることによって、JAXBが実装されます。これらのマッピングを使用すれば、Javaクラス・モデルでスキーマをミラーリングしなくても、複雑なXML構造を管理できます。

1.3 このドキュメントについて

Oracle TopLinkには、その永続性プロバイダとしてEclipseLinkが含まれており、これは、Java Persistence (JPA)仕様のリファレンス実装です。また、拡張機能も数多く含まれています。

1.3.1 その他のリソース

詳細は、次を参照してください:

- Oracle Technology Network (OTN)のOracle TopLink。
http://www.oracle.com/technetwork/middleware/toplink/overview/index.html

- EclipseLink documentation center
http://www.eclipse.org/eclipselink/documentation/

- コアなJAXBプラスEclipseLinkの拡張機能の詳細は、EclipseLink API参照ドキュメント(Javadoc)
http://www.eclipse.org/eclipselink/api/

- Java Architecture for XML Binding (JAXB)の仕様
http://www.jcp.org/en/jsr/detail?id=222

- 多数のJAXBおよびEclipseLink MOXy機能の使用に関する例
http://wiki.eclipse.org/EclipseLink/Examples/MOXy

- JAXBチュートリアル
http://www.oracle.com/technetwork/articles/javase/index-140168.html

2 EclipseLink MOXyランタイム

この章では、EclipseLink MOXyについて紹介および説明します。EclipseLink MOXyは、JAXB仕様で定義される標準ランタイムの1つの実装です。

JAXBプロバイダとしてEclipseLink MOXyを指定する手順は次のとおりです。

- クラスパスに、(Java SE 6に含まれている)JAXB APIとeclipselink.jarを追加します。

- (ドメイン・クラスと同じパッケージにある)jaxb.propertiesファイルを使用します。

この章の内容は次のとおりです。

- EclipseLinkランタイムの指定

- ブートストラップ

- XMLバインディングの使用

- MetadataSourceの使用

- XMLスキーマの生成

- XMLスキーマに対する検証

- イベントの理解

- XPathによるオブジェクトの問合せ

- 既存の文書へのバインド

2.1 EclipseLinkランタイムの指定

EclipseLink MOXyをJAXB実装として使用するには、jaxb.propertiesファイル内でEclipseLink JAXBContextFactoryを特定します。

1. jaxb.propertiesという名前のテキスト・ファイルを作成し、新しいJAXBContextsの構築に使用されるファクトリとして、EclipseLinkのJAXBContextFactoryを次のように指定します。

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

2. このファイルを、モデル・クラスが常駐しているのと同じパッケージ(ディレクトリ)にコピーします。

3. この標準JAXBContext.newInstance(Class... classesToBeBound) APIを使用して、次のようなJAXBContextを作成します。

JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);

アプリケーション・コードを変更する必要がないため、異なるJAXB実装間を容易に切り替えることができます。

JAXBContextを作成するための様々な方法の詳細は、「ブートストラップ」を参照してください。

2.2 ブートストラップ

EclipseLink MOXyには、JAXBContextを作成する際にいくつかのオプションが用意されています。次のものからブートストラップするオプションがあります。

- JAXBの注釈の付いた1つ以上のクラスのリスト

- Javaクラスのマッピングが定義された1つ以上のEclipseLink XMLバインディング・ドキュメントのリスト

- クラスとXMLバインディングの組合せ

- コンテキスト・パスのリスト

- sessions.xmlに定義されているEclipseLinkセッションを参照するセッション名のリスト

2.2.1 JAXBContext APIの使用

(例2-1に示されている)JAXBContextに対するメソッドは、インスタンスの新規作成に使用されます。

例2-1 JAXBContextのメソッド

public static JAXBContext newInstance(Class... classesToBeBound) throws JAXBException

public static JAXBContext newInstance(Class[] classesToBeBound, Map<String,?> properties) throws JAXBException

public static JAXBContext newInstance(String contextPath) throws JAXBException

public static JAXBContext newInstance(String contextPath, ClassLoader classLoader) throws JAXBException

public static JAXBContext newInstance(String contextPath, ClassLoader classLoader, Map<String,?> properties) throws JAXBException

JAXBContextは、次のオプションを受け入れます。

- classesToBeBound: 新しいJAXBContextで認識されるJavaクラスのリスト

- contextPath: マップされたクラスを含むJavaパッケージ名(またはEclipseLinkセッション名)のリスト

- classLoader: マップされたクラスを見つけるために使用されるクラス・ローダー

- properties: 追加プロパティのマップ

例2-1にあるAPIでは、Javaパッケージ/コンテキスト・パスにjaxb.propertiesファイルがあると予想されます。詳細は、「EclipseLinkランタイムの指定」を参照してください。

2.2.2 クラスからのブートストラップ

JAXB注釈付きのJavaクラスのコレクションがある場合は、これらのクラスのリストを直接提供できます。

JAXBContext context = JAXBContext.newInstance(Company.class, Employee.class);

配列内のクラス(たとえば、参照クラスやスーパー・クラス)から読取り可能な他のクラスは、JAXBContextで自動的に認識されます。サブクラスまたはクラスのうち、@XmlTransientとマークされたものは認識されません。

2.2.3 コンテキスト・パスからのブートストラップ

JAXBContextをブートストラップする別の方法は、コンテキスト・パスというStringを使用することです。これは、次のような、マップされたクラスを含むパッケージ名のコロン区切りリストです。

JAXBContext context = JAXBContext.newInstance("example");

この手法を使用した、EclipseLinkがモデル・クラスを検出する方法が、次のようにいくつかあります。

- jaxb.indexファイルの使用

- ObjectFactoryの使用

- MetadataSourceの使用

2.2.3.1 jaxb.indexファイルの使用

コンテキスト・パスには、jaxb.indexというファイルを含めることができ、このファイルは単純なテキスト・ファイルで、現在のパッケージのクラス名を含み、次のJAXBContextに入れられます。

src/example/jaxb.index:

例2-2 jaxb.indexファイルのサンプル

Employee
PhoneNumber

リスト内のクラス(たとえば、参照クラスやスーパー・クラス)から読取り可能な他のクラスは、JAXBContextで自動的に認識されます。サブクラスまたはクラスのうち、@XmlTransientとマークされたものは認識されません。

2.2.3.2 ObjectFactoryの使用

コンテキスト・パスには、ObjectFactoryというクラスを含めることもでき、このクラスは、JAXBが探す特殊なファクトリ・クラスです。このクラスには、モデル内のタイプごとにcreate()メソッドが含まれています。通常の場合、ObjectFactoryはJAXBコンパイラによって生成されますが、手動で作成することもできます。

src/example/ObjectFactory.java:

例2-3 ObjectFactoryのサンプル

@XmlRegistry
public class ObjectFactory {
private final static QName _Employee_QNAME = new QName("", "employee");
private final static QName _PhoneNumber_QNAME = new QName("", "phone-number");
public ObjectFactory() {
}
public EmployeeType createEmployeeType() {
return new EmployeeType();
}
@XmlElementDecl(namespace = "", name = "employee")
public JAXBElement<EmployeeType> createEmployee(EmployeeType value) {
return new JAXBElement<EmployeeType>(_Employee_QNAME, EmployeeType.class, null, value);
}
public PhoneNumberType createPhoneNumberType() {
return new PhoneNumberType();
}

@XmlElementDecl(namespace = "", name = "phone-number")
public JAXBElement<PhoneNumberType> createPhoneNumber(PhoneNumberType value) {
return new JAXBElement<PhoneNumberType>(_PhoneNumber_QNAME, PhoneNumberType.class, null, value);
}

}

2.2.3.3 MetadataSourceの使用

EclipseLink MOXyには、EclipseLinkのMetadataSourceの実装からマッピング情報を取得する機能もあります。この手法を使用し、独自のXmlBindingsを作成する作業を担当します。

例2-4 MetadataSourceのサンプル

package org.eclipse.persistence.jaxb.metadata;
public interface MetadataSource {

/**
* @param properties – The properties passed in to create the JAXBContext
* @param classLoader – The ClassLoader passed in to create the JAXBContext
*
* @return the XmlBindings object representing the metadata
*/
XmlBindings getXmlBindings(Map<String, ?> properties, ClassLoader classLoader);

}

MetadataSourceの使用の詳細は、「MetadataSourceの使用」を参照してください。

2.2.4 EclipseLink XMLバインディングからのブートストラップ

XMLへのクラスのマッピングをさらに制御するために、EclipseLink XMLバインディング・ドキュメントからブートストラップできます。このアプローチを使用すると、EclipseLinkの堅牢なマッピング・フレームワークを活用でき、XMLの複合型をそれぞれ対応するJavaにマップする方法をカスタマイズできます。

実際のドキュメントへのリンクは、特殊キーJAXBContextProperties.OXM_METADATA_SOURCEを次のように使用し、propertiesパラメータを介して渡されます。

例2-5 EclipseLinkバインディング・ドキュメントの使用

InputStream iStream = myClassLoader.getResourceAsStream("example/xml-bindings.xml");

Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, iStream);

JAXBContext context = JAXBContext.newInstance(new Class[]{ Customer.class }, properties);

XMLバインディング形式の詳細は、「XMLバインディングの使用」を参照してください。

2.2.5 注釈付きクラスとXMLバインディングの組合せ

注釈付きクラスからブートストラップする際には、追加のマッピング情報にEclipseLink XMLバインディング・ドキュメントを提供できます。たとえば、モデル・クラスにJAXB-spec-onlyという注釈を付けて、EclipseLink固有のマッピング・カスタマイズをXMLバインディング・ドキュメントに入れることができます(モデル・クラスにEclipseLink注釈をインポートする必要はありません)。

たとえば、例2-6で注釈付きのEmployeeクラスを確認してください。

例2-6 Javaクラスのサンプル

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement

@XmlAccessorType(XmlAccessType.FIELD)
public class Employee {
@XmlElement(name="phone-number")
private PhoneNumber phoneNumber;
...
}

例2-7でXMLバインディングを使用することによって、PhoneNumbersのマーシャリング/アンマーシャリングにEclipseLink XMLAdapterを使用するようにEmployeeをカスタマイズできます。

例2-7 XMLバインディング・ドキュメントの使用

<?xml version="1.0" encoding="US-ASCII"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm">
<java-types>
<java-type name="example.Employee">
<java-attributes>
<xml-element java-attribute="phoneNumber">
<xml-java-type-adapter value="example.util.PhoneNumberProcessor"/>
</xml-element>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>

最後に、例2-8に示されているように、注釈付きクラスのリストとXMLバインディングへのリンクの両方を、JAXBContextに渡します。

例2-8 アプリケーション・コードのサンプル

InputStream iStream = myClassLoader.getResourceAsStream("example/xml-bindings.xml");
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, iStream);

Class[] classes = new Class[] { Company.class, Employee.class };
JAXBContext context = JAXBContext.newInstance(classes, properties);

2.3 XMLバインディングの使用

EclipseLinkには、JAXBの標準注釈に加えて、EclipseLink XMLバインディング・ドキュメントという、メタデータを表現する別の方法も用意されています。XMLバインディングは、マッピング情報を実際のJavaクラスから分離できるだけでなく、次のような高度なメタデータ・タスクにも使用できます。

- 追加のマッピング情報による、既存の注釈の増強またはオーバーライド

- Javaでの注釈をいっさい使用しない、すべてのマッピング情報の外部指定

- 複数のバインディング・ドキュメントにわたるマッピングの定義、Javaの具体的なフィールドに対応しない仮想マッピングの指定、その他

この項では、XMLバインディング形式について説明し、基本的なユースケースをいくつか示します。

2.3.1 XMLバインディング形式の理解

XMLバインディング・ドキュメントとは、Javaタイプ情報、マッピング情報、コンテキスト全体のプロパティなど、JAXBシステムを定義するために必要なすべてのものを指定するXMLです。例となるバインディング・ドキュメントを例2-9に示します。

例2-9 バインディング・ドキュメントの例

<?xml version="1.0" encoding="US-ASCII"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema"
package-name="example" xml-accessor-type="PUBLIC_MEMBER" xml-accessor-order="ALPHABETICAL"
xml-mapping-metadata-complete="false" xml-name-transformer="example.NameGenerator"
supported-versions="2.4" >

<xml-schema element-form-default="QUALIFIED">
<xml-ns prefix="ns1" namespace-uri="http://www.example.org/type" />
</xml-schema>
<java-types>
<java-type name="Employee">
<xml-type namespace="http://www.example.org/type" />
<java-attributes>
<xml-attribute java-attribute="empId" xml-path="@id" />
<xml-element java-attribute="empName" name="name" />
<xml-element java-attribute="salary" />
<xml-element java-attribute="type" type="EmployeeType" />
</java-attributes>
</java-type>
<java-type name="Company">
<xml-root-element name="company" />
<xml-attribute java-attribute="empId" xml-path="@id" />
<xml-element java-attribute="empName" name="name" />
<java-attributes>
<xml-element java-attribute="employees" name="employee"
type="example.Employee" container-type="java.util.ArrayList" />
</java-attributes>
</java-type>
</java-types>

<xml-registries>
<xml-registry name="example.ObjectFactory">
<xml-element-decl java-method="createEmpleado"
name="empleado" type="example.Employee" />
<xml-element-decl java-method="createCorporacion"
name="corporacion" type="example.Company" />
</xml-registry>
</xml-registries>

<xml-enums>
<xml-enum java-enum="EmployeeType" value="java.lang.String">
<xml-enum-value java-enum-value="CONTRACT">CONTRACT</xml-enum-value>
<xml-enum-value java-enum-value="PART_TIME">PART_TIME</xml-enum-value>
<xml-enum-value java-enum-value="FULL_TIME">FULL_TIME</xml-enum-value>
</xml-enum>
</xml-enums>
</xml-bindings>

表2-1 バインディング・ドキュメントの属性

属性

説明

<xml-bindings>

XMLバインディング・ドキュメントのルート。ここでは、クラスのpackage-nameなど、JAXBシステムの最上位レベルのプロパティを定義したり、デフォルトのxml-accessor-typeを指定するなどの作業もできます。

<xml-schema>

JAXBシステムのスキーマ・レベルに関連するプロパティを定義します。JAXBの@XmlSchema注釈に対応しています。

<java-types>

Javaクラスごとにマッピング情報を定義します。

<xml-enums>

Javaタイプで使用できるJava列挙を定義します。

<xml-registries>

JAXBシステムで使用するためのObjectFactoryを定義します。

2.3.2 XMLバインディングを使用したブートストラップ

JAXBContextをインスタンス化する際、バインディング・ドキュメントへのリンクは、特殊キーJAXBContextProperties.OXM_METADATA_SOURCEを使用し、propertiesパラメータを介して渡されます。このキーの値は、次のいずれかの形式によるバインディング・ドキュメントへのハンドルとなります。

- java.io.File

- java.io.InputStream

- java.io.Reader

- java.net.URL

- javax.xml.stream.XMLEventReader

- javax.xml.stream.XMLStreamReader

- javax.xml.transform.Source

- org.w3c.dom.Node

- org.xml.sax.InputSource

複数のXMLバインディング・ドキュメントからブートストラップする場合は、次のようになります。

- 前述の入力のマップがサポートされ、Javaパッケージ名がキーとなります。

- 前述の入力のリストも受け入れられます(<xml-bindings>にはパッケージ属性が必要)。

2.3.3 XMLバインディングの注釈付き使用

XMLバインディング・ドキュメントの用途としては、JAXB注釈と併用されるのが最も一般的です。Javaドメイン・クラスの編集は許可されていないが、別のマッピング機能を追加したいというような状況も考えられます。または、ドメイン・モデルにEclipseLinkコードをインポートすることはやめたいが、MOXyの高度なマッピング機能は引き続き利用したいということも考えられます。コンテキストの作成中にバインディング・メタデータが提供されると、そのマッピング情報にJAXB注釈情報が組み合されます。

たとえば、例2-10に示されている単純なJAXBドメイン・クラスおよびそのデフォルトのJAXB XML表現を検討してください。

例2-10 JAXBドメイン・クラスとXMLのサンプル

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlAttribute
private Integer custId;
private String name;
private Double salary;
private byte[] picture;
...
}

<?xml version="1.0" encoding="UTF-8"?>
<customer custId="15">
<name>Bob Dobbs</name>
<salary>51727.61</salary>
<picture>AgQIECBA</picture>
</customer>

ここで、マッピングを次のように変更するとします。

- custIdのXML要素名をcustomer-idに変更する

- このクラスのルート要素名をcustomer-infoに変更する

- 16進のbinary形式でのpicture-hexとしてピクチャをXMLに書き込み、独自のカスタム・コンバータであるMyHexConverterを使用する

例2-11に示すように、これら3つのカスタマイズをXMLバインディング・ドキュメントで指定できます。

例2-11 カスタマイズされたXMLバインディング

<?xml version="1.0" encoding="US-ASCII"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="example">

<java-types>
<java-type name="Customer">
<xml-root-element name="customer-info" />
<java-attributes>
<xml-attribute java-attribute="custId" name="customer-id" />
<xml-element java-attribute="picture" name="picture-hex">
<xml-schema-type name="hexBinary" />
<xml-java-type-adapter
value="example.adapters.MyHexConverter" />
</xml-element>
</java-attributes>
</java-type>
</java-types>

</xml-bindings>

バインディングは、JAXBコンテキストの作成中に提供される必要があります。バインディング情報は、properties引数を介して次のように渡されます。

例2-12 バインディングの提供

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream iStream = classLoader.getResourceAsStream("metadata/xml-bindings.xml");

Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, iStream);

JAXBContext ctx = JAXBContext.newInstance(new Class[] { Customer.class }, properties);

バインディングを提供する際には、JAXBコンテキストの作成中に、Oracle TopLinkで次の処理が行われます。

1. Customer.classが分析され、JAXBマッピングが通常どおりに生成されます。

2. 次にバインディング・ドキュメントが分析され、元のJAXBマッピングが、バインディング・ドキュメント内の情報とマージされます。

XMLバインディングを適用すると、次のように、目的のXML表現が得られます。

<?xml version="1.0" encoding="UTF-8"?>
<customer-info customer-id="15">
<name>Bob Dobbs</name>
<salary>51727.61</salary>
<picture-hex>020408102040</picture-hex>
</customer-info>

2.3.4 複数のバインディング・ドキュメントの使用

バージョン2.3以降のEclipseLinkでは、複数のXMLバインディング・ドキュメントのマッピング情報を使用できます。この手法を使用し、メタデータを好きなように分割できます。

例2-13 XMLバインディングのリストの使用

...
FileReader file1 = new FileReader("base-bindings.xml");
FileReader file2 = new FileReader("override-bindings.xml");

List<Object> fileList = new ArrayList<Object>();
fileList.add(file1);
fileList.add(file2);

Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, fileList);

JAXBContext ctx = JAXBContext.newInstance(new Class[] { Customer.class }, properties);

...

バインディング・ドキュメントのリストを使用するときには、バインディングのセットごとのパッケージを示すために、それぞれのバインディング・ドキュメントで<xml-bindings>のパッケージ属性を定義する必要があります。

例2-14 複数のパッケージでの1つのマップの使用

...

FileReader fooFile1 = new FileReader("foo/base-bindings.xml");
FileReader fooFile2 = new FileReader("foo/override-bindings.xml");

List<Object> fooFileList = new ArrayList<Object>();
fooFileList.add(fooFile1);
fooFileList.add(fooFile2);

FileReader barFile1 = new FileReader("bar/base-bindings.xml");
FileReader barFile2 = new FileReader("bar/override-bindings.xml");

List<Object> barFileList = new ArrayList<Object>();
barFileList.add(barFile1);
barFileList.add(barFile2);

Map<String, List> metadataMap = new HashMap<String, List>();
metadataMap.put("foo", fooFileList);
metadataMap.put("bar", barFileList);

properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, metadataMap);

JAXBContext ctx = JAXBContext.newInstance(new Class[] { Customer.class }, properties);

...

2.3.5 オーバーライド・ルールの理解

同じパッケージにメタデータのソースが複数あった場合は、メタデータの完全セットをマージすることによって、マッピングの統一セットが作成されます。まずJavaクラスの注釈が処理され、次に、XMLバインディング情報があれば適用されます。バインディングの指定順序は関連性があり、後続のドキュメント内の値によって、以前のドキュメントで定義された値がオーバーライドされます。

マージに対して次のルールが使用されます。

- xml-schema

- namespace、elementform、attributeformなどの値では、後のファイルによってオーバーライドされます。

- XmlNsのネームスペース宣言のリストは、すべてのファイルのすべてのエントリを含む単一のリストにマージされます。
エントリが競合している(複数のネームスペースに同じ接頭辞がバインドされている)場合は、最後のファイルによって、以前のファイルの宣言がオーバーライドされます。

- java-types

- マージされたバインディングには、すべてのバインディング・ファイルの一意のjava-typeエントリがすべて含まれます。

- 同じjava-typeが複数のファイルに出現している場合、後のファイルで設定されている値があれば、それによって、以前のファイルの値がオーバーライドされます。

- それぞれのjava-typeに対するプロパティは、統一リストにマージされます。同じプロパティが複数のファイルで参照されている場合は例外です。

- クラス・レベルのXmlJavaTypeAdpaterエントリは、後のバインディング・ファイルで指定されている場合はオーバーライドされます。

- クラス・レベルのXmlSchemaTypesによって、マージされたリストが作成されます。同じタイプのエントリが、このレベルで複数のバインディング・ファイルにリストされている場合は、最後のファイルのエントリによって、以前のエントリがすべてオーバーライドされます。

- xml-enums

- マージされたバインディングには、すべてのバインディング・ファイルの一意のxml-enumエントリがすべて含まれます。

- java-enumが重複する場合に備えて、XmlEnumValuesのマージされたリストが作成されます。同じenumファセットのエントリが複数のファイルに出現している場合は、最後のファイルによって、そのファセットの値がオーバーライドされます。

- xml-java-type-adapters

- パッケージ・レベルのJavaタイプ・アダプタは、単一のリストにマージされます。複数のファイルで同じクラスに対してアダプタが指定されている場合は、最後のファイルのエントリが優先されます。

- xml-registries

- 一意のXmlRegistryエントリはそれぞれ、XmlRegistriesのマージされた最終的なリストに追加されます。

- XmlRegistryエントリが重複する場合に備えて、XmlElementDeclsのマージされたリストが作成されます。
同じXmlRegistryクラスに対するXmlElementDeclが複数のバインディング・ファイルに出現している場合は、そのXmlElementDeclは、後のバインディングのものに置換されます。

- xml-schema-types

- XmlSchemaTypeエントリは、統一リストにマージされます。

- 同じjava-typeに対するXmlSchemaTypeエントリが、パッケージ・レベルで複数のバインディング・ファイルに出現している場合は、マージされたバインディングには、最後に指定されたファイルのエントリのみが含まれます。

2.3.6 完全なメタデータの使用

メタデータのすべてをXMLバインディングに格納し、JavaクラスにJAXB注釈があったら無視する場合は、バインディング・ドキュメントの<xml-bindings>要素にxml-mapping-metadata-complete属性を含めることができます。デフォルトのJAXBマッピングは引き続き生成され(JAXBでまったく注釈が付けられていないクラスを使用している場合と同じ)、XMLバインディングに定義されているマッピング・データがあれば適用されます。

これは、まったく異なる2つのXML表現に同じJavaクラスをマップするような場合に使用できます。その場合、実際のJavaクラスに対する注釈によって最初のXML表現が定義され、次に、xml-mapping-metadata-complete="true"によって、2番目のXML表現をXMLバインディング・ドキュメントに定義できます。これによって本質的に、Javaクラスを再マップするための「空白のキャンバス」が得られます。

JAXBで生成されるデフォルトのマッピングを無視する場合は、<java-type>要素でxml-accessor-type="NONE"と指定できます。この手法を使用した場合、適用されるのは、バインディング・ドキュメントで明示的に定義されたマッピングのみです。

前述のCustomerの例を使用すると、この後の例では、xml-mapping-metadata-completeを使用したときに生成されるXML表現が示されます。

例2-15 Customerクラスのサンプル

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlAttribute
private Integer custId;
private String name;
private Double salary;
private byte[] picture;
...
}

例2-16 XMLバインディング

<?xml version="1.0" encoding="US-ASCII"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="example" xml-mapping-metadata-complete="true">

<java-types>
<java-type name="Customer">
<xml-root-element />
<java-attributes>
<xml-attribute java-attribute="name" name="customer-name" />
</java-attributes>
</java-type>
</java-types>

</xml-bindings>

例2-17 XML表現

<?xml version="1.0" encoding="UTF-8"?>
<customer>
<custId>15</custId>
<customer-name>Bob Dobbs</customer-name>
<picture>AgQIECBA</picture>
<salary>51727.61</salary>
</customer>

- custIdにデフォルトのJAXBマッピングが生成される(custIdは、Javaフィールドに注釈が付けられていないかのようにXML要素となる)

- 名前要素の名前がcustomer-nameに変更される

- pictureとsalaryにデフォルトのJAXBマッピングが生成される

例2-18 XMLバインディング(xml-accessor-type="NONE"と指定されている)

<?xml version="1.0" encoding="US-ASCII"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="example" xml-mapping-metadata-complete="true">

<java-types>
<java-type name="Customer" xml-accessor-type="NONE">
<xml-root-element />
<java-attributes>
<xml-attribute java-attribute="name" name="customer-name" />
</java-attributes>
</java-type>
</java-types>

</xml-bindings>

例2-19 XML表現

<?xml version="1.0" encoding="UTF-8"?>
<customer>
<customer-name>Bob Dobbs</customer-name>
</customer>

- xml-accessor-type="NONE"と指定すると、デフォルトのマッピングは生成されなくなる

- XML表現には、XMLバインディング・ドキュメントに定義されているマッピングのみが含まれている

2.3.7 仮想マッピングの使用

XMLバインディングは仮想マッピングを指定する際にも使用でき、仮想マッピングとは、具体的なJavaフィールドに対応しないマッピングです。たとえば、特定のマッピングのためのデータ保持の基礎となる構造として、HashMapを使用する場合があります。仮想マッピングの使用の詳細は、「仮想アクセス・メソッドの使用」を参照してください。

2.4 MetadataSourceの使用

EclipseLink 2.3で導入されたMetadataSourceは、EclipseLinkメタデータの提供を担当します。これにより、マッピング情報をアプリケーションの外部に保存して、アプリケーションのJAXBContextが作成またはリフレッシュされるときにマッピング情報を取得できるようになります。

2.4.1 MetadataSourceの実装

独自のMetadataSourceを実装するために、次の操作ができます。

- org.eclipse.persistence.jaxb.metadata.MetadataSourceインタフェースを実装するクラスを新規作成します。

- org.eclipse.persistence.jaxb.metadata.MetadataSourceAdapterクラスを拡張するクラスを新規作成します。このメソッドを使用することのほうが望ましいと考えられます。将来、インタフェースに追加する作業が不要になるからです。

どちらの場合でも、次のメソッドの実装を担当します。

例2-20 XMlBindingsメソッドの実装

/**
* Retrieve XmlBindings according to the JAXBContext bootstrapping information.
*
* @param properties - The properties passed in to create the JAXBContext
* @param classLoader - The ClassLoader passed in to create the JAXBContext
* @return the XmlBindings object representing the metadata
*/
XmlBindings getXmlBindings(Map<String, ?> properties, ClassLoader classLoader);

2.4.2 XmlBindingsオブジェクトの使用

内部的には、EclipseLinkメタデータはXmlBindingsオブジェクトに格納され、このオブジェクト自体はJAXBでマップされます。つまり、JAXBアンマーシャラを実際に使用して、外部メタデータを読み取り、そこからXmlBindingsを作成できます。

例2-21 XmlBindingsオブジェクトのサンプル

package example;

import org.eclipse.persistence.jaxb.xmlmodel.XmlBindings;
...
JAXBContext xmlBindingsContext = JAXBContext.newInstance("org.eclipse.persistence.jaxb.xmlmodel");
FileReader bindingsFile = new FileReader("xml-bindings.xml");
XmlBindings bindings = (XmlBindings) xmlBindingsContext.createUnmarshaller().unmarshal(bindingsFile);

2.4.3 MetadataSourceの指定

JAXBContextを作成する際にMetadataSourceを使用するには、キーJAXBContextProperties.OXM_METADATA_SOURCEを使用してプロパティ・マップに追加します。

例2-22 プロパティ・マップへのMetadataSourceの追加

MetadataSource metadataSource = new MyMetadataSource();

Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, metadataSource);

JAXBContext jc = JAXBContext.newInstance(new Class[] { Customer.class }, properties);

2.4.4 MetadataSourceの例

次の例では、URLからアンマーシャリングすることによって、XmlBindingsオブジェクトを作成します。

例2-23 XmlBindingsオブジェクトのサンプル

package example;

import java.net.URL;
import java.util.Map;

import javax.xml.bind.JAXBContext;

import org.eclipse.persistence.jaxb.metadata.MetadataSourceAdapter;
import org.eclipse.persistence.jaxb.xmlmodel.XmlBindings;

public class MyMetadataSource extends MetadataSourceAdapter {

private JAXBContext bindingsContext;
private URL bindingsUrl;

private final String XML_BINDINGS_PACKAGE = "org.eclipse.persistence.jaxb.xmlmodel";
private final String METADATA_URL = "http://www.example.com/private/metadata/xml-bindings.xml";
public MyMetadataSource() {
try {
bindingsContext = JAXBContext.newInstance(XML_BINDINGS_PACKAGE);
bindingsUrl = new URL(METADATA_URL);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

@Override
public XmlBindings getXmlBindings(Map<String, ?> properties, ClassLoader classLoader) {
try {
Unmarshaller u = bindingsContext.createUnmarshaller();
XmlBindings bindings = (XmlBindings) u.unmarshal(bindingsUrl);
return bindings;
} catch (Exception e) {
throw new RuntimeException(e);
}
}

}

2.4.5 プログラムによるXmlBindingsの構築

独自のXmlBindingsオブジェクトをコードでゼロから構築するというオプションもあります。次の例では、ロケール固有の名前を使用するようにAddressクラスのpCodeフィールドを変更します。

例2-24 XmlBindingsオブジェクトのサンプル

package example;

import java.util.Locale;
import java.util.Map;

import org.eclipse.persistence.jaxb.metadata.MetadataSourceAdapter;
import org.eclipse.persistence.jaxb.xmlmodel.JavaType;
import org.eclipse.persistence.jaxb.xmlmodel.JavaType.JavaAttributes;
import org.eclipse.persistence.jaxb.xmlmodel.ObjectFactory;
import org.eclipse.persistence.jaxb.xmlmodel.XmlBindings;
import org.eclipse.persistence.jaxb.xmlmodel.XmlBindings.JavaTypes;
import org.eclipse.persistence.jaxb.xmlmodel.XmlElement;

public class AddressMetadataSource extends MetadataSourceAdapter {

private ObjectFactory factory;
private XmlBindings xmlBindings;

public AddressMetadataSource() {
factory = new ObjectFactory();

xmlBindings = new XmlBindings();
xmlBindings.setPackageName("example");
xmlBindings.setJavaTypes(new JavaTypes());
}

@Override
public XmlBindings getXmlBindings(Map<String, ?> properties, ClassLoader classLoader) {
JavaType javaType = new JavaType();
javaType.setName("Address");
javaType.setJavaAttributes(new JavaAttributes());

XmlElement pCodeElement = new XmlElement();
pCodeElement.setJavaAttribute("pCode");

String country = Locale.getDefault().getCountry();
if (country.equals(Locale.US.getCountry())) {
pCodeElement.setName("zip-code");
} else if (country.equals(Locale.UK.getCountry())) {
pCodeElement.setName("post-code");
} else if (country.equals(Locale.CANADA.getCountry())) {
pCodeElement.setName("postal-code");
}

javaType.getJavaAttributes().getJavaAttribute().add(factory.createXmlElement(pCodeElement));

xmlBindings.getJavaTypes().getJavaType().add(javaType);
return xmlBindings;
}

}

2.5 XMLスキーマの生成

Javaオブジェクト・モデルからXMLスキーマを生成する手順は次のとおりです。

1. javax.xml.bind.SchemaOutputResolverを拡張するクラスを作成します。

private class MySchemaOutputResolver extends SchemaOutputResolver {

public Result createOutput(String uri, String suggestedFileName) throws IOException {
File file = new File(suggestedFileName);
StreamResult result = new StreamResult(file);
result.setSystemId(file.toURI().toURL().toString());
return result;
}

}

2. このクラスのインスタンスをJAXBContextとともに使用して、生成されたXMLスキーマを取り込みます。

Class[] classes = new Class[4];
classes[0] = org.example.customer_example.AddressType.class;
classes[1] = org.example.customer_example.ContactInfo.class;
classes[2] = org.example.customer_example.CustomerType.class;
classes[3] = org.example.customer_example.PhoneNumber.class;
JAXBContext jaxbContext = JAXBContext.newInstance(classes);

SchemaOutputResolver sor = new MySchemaOutputResolver();
jaxbContext.generateSchema(sor);

2.6 XMLスキーマに対する検証

マーシャリング時およびアンマーシャリング時に、XMLスキーマに対してオブジェクトを検証する場合は、JAXBのValidationEventHandlerを利用できます。

例2-25 XMLスキーマのサンプル

この例では、次のXMLスキーマに対してオブジェクトを検証します。

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

<xs:element name="customer">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="stringMaxSize5"/>
<xs:element ref="phone-number" maxOccurs="2"/>
</xs:sequence>
</xs:complexType>
</xs:element>

<xs:element name="phone-number">
<xs:complexType>
<xs:sequence/>
</xs:complexType>
</xs:element>

<xs:simpleType name="stringMaxSize5">
<xs:restriction base="xs:string">
<xs:maxLength value="5"/>
</xs:restriction>
</xs:simpleType>

</xs:schema>

次の制約に注目してください。

- 顧客の名前は、5文字以内にする必要があります。

- 顧客に割り当てらることのできる電話番号は2つまでです。

Customerクラスを次に示します。このクラスには検証コードがないことに注目してください。

例2-26 Customerクラスのサンプル

package example;

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Customer {
private String name;

private List<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>();

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@XmlElement(name="phone-number")
public List<PhoneNumber> getPhoneNumbers() {
return phoneNumbers;
}

public void setPhoneNumbers(List<PhoneNumber> phoneNumbers) {
this.phoneNumbers = phoneNumbers;
}
}

2.6.1 ValidationEventHandlerの使用

ValidationEventHandlerの独自サブクラスを提供することによって、JAXB検証イベントを受信できます。このイベントは、ValidationEventのインスタンスとして表現され、問題の詳細を数多く提供します。このデータは、SAXParseExceptionから入手されるものに類似しています。

- handleEventメソッドからfalseが返されると、JAXB操作が停止します。

- trueが返されると、可能であればメソッドを続行できます。

例2-27では、単にイベントが受信された際のデータを出力しています。

例2-27 ValidationEventHandlerのサンプル

package example;

import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;

public class MyValidationEventHandler implements ValidationEventHandler {

public boolean handleEvent(ValidationEvent event) {
System.out.println("\nEVENT");
System.out.println("SEVERITY:  " + event.getSeverity());
System.out.println("MESSAGE:  " + event.getMessage());
System.out.println("LINKED EXCEPTION:  " + event.getLinkedException());
System.out.println("LOCATOR");
System.out.println("    LINE NUMBER:  " + event.getLocator().getLineNumber());
System.out.println("    COLUMN NUMBER:  " + event.getLocator().getColumnNumber());
System.out.println("    OFFSET:  " + event.getLocator().getOffset());
System.out.println("    OBJECT:  " + event.getLocator().getObject());
System.out.println("    NODE:  " + event.getLocator().getNode());
System.out.println("    URL:  " + event.getLocator().getURL());
return true;
}

}

2.6.2 検証の有効化

ValidationEventHandlerの実装を提供することに加えて、MarshallerまたはUnmarshallerにSchemaのインスタンスを設定する必要があります。

例2-28 Javaコードのサンプル

package example;

import java.io.File;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

public class UnmarshalDemo {

public static void main(String[] args) throws Exception {
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = sf.newSchema(new File("customer.xsd"));

JAXBContext jc = JAXBContext.newInstance(Customer.class);

Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setSchema(schema);
unmarshaller.setEventHandler(new MyValidationEventHandler());
Customer customer = (Customer) unmarshaller.unmarshal(new File("input.xml"));
}

}

2.6.2.1 入力(input.xmlファイル)

<customer>
<name>Jane Doe</name>
<phone-number/>
<phone-number/>
<phone-number/>
</customer>

2.6.2.2 出力

アンマーシャリング中に実行された検証によって、3つのイベントが提起されました。最初の2つのイベントは、長すぎるname要素のテキスト値に関連しています。3番目のイベントは、余分のphone-number要素に関連しています。

EVENT
SEVERITY:  1
MESSAGE:  cvc-maxLength-valid: Value 'Jane Doe' with length = '8' is not facet-valid with respect
to maxLength '5' for type 'stringWithMaxSize5'.
LINKED EXCEPTION:  org.xml.sax.SAXParseException: cvc-maxLength-valid: Value 'Jane Doe' with length = '8'
is not facet-valid with respect to maxLength '5' for type 'stringWithMaxSize5'.
LOCATOR
LINE NUMBER:  3
COLUMN NUMBER:  25
OFFSET:  -1
OBJECT:  null
NODE:  null
URL:  null

EVENT
SEVERITY:  1
MESSAGE:  cvc-type.3.1.3: The value 'Jane Doe' of element 'name' is not valid.
LINKED EXCEPTION:  org.xml.sax.SAXParseException: cvc-type.3.1.3: The value 'Jane Doe' of element
'name' is not valid.
LOCATOR
LINE NUMBER:  3
COLUMN NUMBER:  25
OFFSET:  -1
OBJECT:  null
NODE:  null
URL:  null

EVENT
SEVERITY:  1
MESSAGE:  cvc-complex-type.2.4.d: Invalid content was found starting with element 'customer'. No child
element '{phone-number}' is expected at this point.
LINKED EXCEPTION:  org.xml.sax.SAXParseException: cvc-complex-type.2.4.d: Invalid content was found starting
with element 'customer'. No child element '{phone-number}' is expected at this point.
LOCATOR
LINE NUMBER:  7
COLUMN NUMBER:  12
OFFSET:  -1
OBJECT:  null
NODE:  null
URL:  null

2.7 イベントの理解

JAXBには、マーシャリング・プロセス中およびアンマーシャリング・プロセス中に、イベント・コールバックを取得するメカニズムがいくつか用意されています。マップされたオブジェクトにコールバック・メソッドを直接指定するか、または別のListenerクラスを定義し、JAXBランタイムに登録できます。

2.7.1 JAXBでマップされたオブジェクトへのイベント・リスナー・メソッドの追加

JAXBでマップしたオブジェクトのいずれに対しても、特殊なメソッドをいくつか指定して、そのオブジェクトがマーシャリングまたはアンマーシャリングされたときに、イベント通知を受信できるようにするというオプションもあります。これらのメソッドには次のシグネチャが必要です。

/**
* Invoked by Marshaller after it has created an instance of this object.
*/
void beforeMarshal(Marshaller m);

/**
* Invoked by Marshaller after it has marshalled all properties of this object.
*/
void afterMarshal(Marshaller m);

/**
* This method is called immediately after the object is created and before the unmarshalling of this
* object begins. The callback provides an opportunity to initialize JavaBean properties prior to unmarshalling.
*/
void beforeUnmarshal(Unmarshaller u, Object parent);

/**
* This method is called after all the properties (except IDREF) are unmarshalled for this object,
* but before this object is set to the parent object.
*/
void afterUnmarshal(Unmarshaller u, Object parent);

次の例は、Companyオブジェクトが処理されるたびにログに書き込む方法を示しています。

例2-29 イベント・リスナーのサンプル

package example;

import java.util.Date;
import java.util.logging.Logger;
import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Company {

@XmlAttribute
private String id;

void beforeMarshal(Marshaller m) {
Logger.getLogger("example").info("COMPANY:[id=" + id + "] " + Thread.currentThread());
}

void afterMarshal(Marshaller m) {
Logger.getLogger("example").info("COMPANY:[id=" + id + "] " + Thread.currentThread());
}

void beforeUnmarshal(Unmarshaller u, Object parent) {
Logger.getLogger("example").info("COMPANY:[id=" + id + "] " + Thread.currentThread());
}

void afterUnmarshal(Unmarshaller u, Object parent) {
Logger.getLogger("example").info("COMPANY:[id=" + id + "] " + Thread.currentThread());
}

}

2.7.2 マーシャラおよびアンマーシャラに対するリスナーの登録

JAXBのマーシャラ・インタフェースとアンマーシャラ・インタフェースは両方ともsetListener()メソッドを定義し、それによって、独自のカスタム・リスナーを設定して、マーシャリング・イベントおよびアンマーシャリング・イベントを遮断できます。

package javax.xml.bind;

public interface Marshaller {
...
public static abstract class Listener {
/**
* Callback method invoked before marshalling from source to XML.
*
* This method is invoked just before marshalling process starts to marshal source.
* Note that if the class of source defines its own beforeMarshal method,
* the class specific callback method is invoked just before this method is invoked.
*
* @param source instance of JAXB mapped class prior to marshalling from it.
*/
public void beforeMarshal(Object source) {}

/**
* Callback method invoked after marshalling source to XML.
*
* This method is invoked after source and all its descendants have been marshalled.
* Note that if the class of source defines its own afterMarshal method,
* the class specific callback method is invoked just before this method is invoked.
*
* @param source instance of JAXB mapped class after marshalling it.
*/
public void afterMarshal(Object source) {}
}
}

package javax.xml.bind;

public interface Unmarshaller {
...
public static abstract class Listener {

/**
* Callback method invoked before unmarshalling into target.
*
* This method is invoked immediately after target was created and
* before the unmarshalling of this object begins. Note that
* if the class of target defines its own beforeUnmarsha method,
* the class specific callback method is invoked before this method is invoked.
*
* @param target non-null instance of JAXB mapped class prior to unmarshalling into it.
* @param parent instance of JAXB mapped class that will eventually reference target.
*               null when target is root element.
*/
public void beforeUnmarshal(Object target, Object parent) {}

/**
* Callback method invoked after unmarshalling XML data into target.
*
* This method is invoked after all the properties (except IDREF) are unmarshalled into target,
* but before target is set into its parent object.
* Note that if the class of target defines its own afterUnmarshal method,
* the class specific callback method is invoked before this method is invoked.
*
* @param target non-null instance of JAXB mapped class prior to unmarshalling into it.
* @param parent instance of JAXB mapped class that will reference target.
*               null when target is root element.
*/
public void afterUnmarshal(Object target, Object parent) {}
}
}

この例で実行するロギングは、前述のものと同じですが、汎用的なListenerクラスを使用する点が異なります。これによって、システム内のJAXBオブジェクトをすべてログに記録しやすくなります。

例2-30 リスナー・クラスを使用したロギング

package example;

import java.util.logging.Logger;

private class MarshalLogger extends Marshaller.Listener {
@Override
public void afterMarshal(Object source) {
Logger.getLogger("example").info(source + " "   + Thread.currentThread());
}

@Override
public void beforeMarshal(Object source) {
Logger.getLogger("example").info(source + " "   + Thread.currentThread());
}
}

package example;

import java.util.logging.Logger;

private class UnmarshalLogger extends Unmarshaller.Listener {
@Override
public void afterUnmarshal(Object target, Object parent) {
Logger.getLogger("example").info(target + " "   + Thread.currentThread());
}

@Override
public void beforeUnmarshal(Object target, Object parent) {
Logger.getLogger("example").info(target + " "   + Thread.currentThread());
}
}

次のコードによってリスナーが設定されます。

Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setListener(new MarshalLogger());

Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
unmarshaller.setListener(new UnmarshalLogger());

次に示すのは、典型的なマーシャリング/アンマーシャリングの例で、クラス・レベルとマーシャラ/アンマーシャラ・レベル両方のイベント出力を示しています。

Jun 2, 2011 6:31:59 PM example.Company beforeMarshal
INFO: COMPANY:[id=Zoltrix] Thread[main,5,main]
Jun 2, 2011 6:31:59 PM example.Tester$MarshalLogger beforeMarshal
INFO: example.Company@10e790c Thread[main,5,main]
Jun 2, 2011 6:31:59 PM example.Tester$MarshalLogger beforeMarshal
INFO: example.Employee@1db7df8 Thread[main,5,main]
Jun 2, 2011 6:31:59 PM example.Tester$MarshalLogger afterMarshal
INFO: example.Employee@1db7df8 Thread[main,5,main]
Jun 2, 2011 6:31:59 PM example.Tester$MarshalLogger beforeMarshal
INFO: example.Employee@3570b0 Thread[main,5,main]
Jun 2, 2011 6:31:59 PM example.Tester$MarshalLogger afterMarshal
INFO: example.Employee@3570b0 Thread[main,5,main]
Jun 2, 2011 6:31:59 PM example.Tester$MarshalLogger beforeMarshal
INFO: example.Employee@79717e Thread[main,5,main]
Jun 2, 2011 6:31:59 PM example.Tester$MarshalLogger afterMarshal
INFO: example.Employee@79717e Thread[main,5,main]
Jun 2, 2011 6:31:59 PM example.Company afterMarshal
INFO: COMPANY:[id=Zoltrix] Thread[main,5,main]
Jun 2, 2011 6:31:59 PM example.Tester$MarshalLogger afterMarshal
INFO: example.Company@10e790c Thread[main,5,main]
Jun 2, 2011 6:31:59 PM example.Company beforeUnmarshal
INFO: COMPANY:[id=null] Thread[main,5,main]
Jun 2, 2011 6:31:59 PM example.Tester$UnmarshalLogger beforeUnmarshal
INFO: example.Company@f0c0d3 Thread[main,5,main]
Jun 2, 2011 6:31:59 PM example.Tester$UnmarshalLogger beforeUnmarshal
INFO: example.Employee@4f80d6 Thread[main,5,main]
Jun 2, 2011 6:31:59 PM example.Tester$UnmarshalLogger afterUnmarshal
INFO: example.Employee@4f80d6 Thread[main,5,main]
Jun 2, 2011 6:31:59 PM example.Tester$UnmarshalLogger beforeUnmarshal
INFO: example.Employee@1ea0252 Thread[main,5,main]
Jun 2, 2011 6:31:59 PM example.Tester$UnmarshalLogger afterUnmarshal
INFO: example.Employee@1ea0252 Thread[main,5,main]
Jun 2, 2011 6:31:59 PM example.Tester$UnmarshalLogger beforeUnmarshal
INFO: example.Employee@3e89c3 Thread[main,5,main]
Jun 2, 2011 6:31:59 PM example.Tester$UnmarshalLogger afterUnmarshal
INFO: example.Employee@3e89c3 Thread[main,5,main]
Jun 2, 2011 6:31:59 PM example.Company afterUnmarshal
INFO: COMPANY:[id=Zoltrix] Thread[main,5,main]
Jun 2, 2011 6:31:59 PM example.Tester$UnmarshalLogger afterUnmarshal
INFO: example.Company@f0c0d3 Thread[main,5,main]

2.8 XPathによるオブジェクトの問合せ

EclipseLink MOXyでは、従来のJavaのアクセス方法を使用してオブジェクトの値を取得および設定する方法に加えて、XPath文を使用して値にアクセスすることもできます。EclipseLinkのJAXBContextには、XPathで値を取得および設定できるようにするための特殊なAPIがあります。

たとえば、次のXML文書を考えてみます。

<customer id="1141">
<first-name>Jon</first-name>
<last-name>Smith</last-name>
<phone-number>
<area-code>515</area-code>
<number>2726652</number>
</phone-number>
</customer>

典型的なアプリケーション・コードは、次のようになる場合があります。

Customer customer = (Customer) jaxbContext.createUnmarshaller().unmarshal(instanceDoc);
...
int customerId = customer.getId();
customer.setFirstName("Bob");
customer.getPhoneNumber().setAreaCode("555");
...
jaxbContext.createMarshaller().marshal(customer, System.out);

かわりに、XPathを使用してこれらの値にアクセスできます。

Customer customer = (Customer) jaxbContext.createUnmarshaller().unmarshal(instanceDoc);
...
int customerId = jaxbContext.getValueByXPath(customer, "@id", null, Integer.class);
jaxbContext.setValueByXPath(customer, "first-name/text()", null, "Bob");
jaxbContext.setValueByXPath(customer, "phone-number/area-code/text()", null, "555");
...
jaxbContext.createMarshaller().marshal(customer, System.out);

2.9 既存の文書へのバインド

(JAXB 2.0で導入された)JAXBバインダ・インタフェースでは、アイテムの一部のみがマップされている場合でも、XML文書全体を保持できます。

通常の場合、アンマーシャラを使用してXML文書をオブジェクトにロードしたり、マーシャラを使用してオブジェクトを元のXMLに保存すると、マップされていないコンテンツは失われます。

JAXBContextからバインダを作成して、DOM形式のXMLと対話できます。バインダにより、Javaオブジェクトとそれに対応するDOMノードとの間の関連付けが保持されます。

例2-31 バインダの使用

import java.io.File;
import javax.xml.bind.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.*;

public class BinderDemo {

public static void main(String[] args) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
File xml = new File("input.xml");
Document document = db.parse(xml);

JAXBContext jc = JAXBContext.newInstance(Customer.class);

Binder<Node> binder = jc.createBinder();
Customer customer = (Customer) binder.unmarshal(document);
customer.getAddress().setStreet("2 NEW STREET");
PhoneNumber workPhone = new PhoneNumber();
workPhone.setType("work");
workPhone.setValue("555-WORK");
customer.getPhoneNumbers().add(workPhone);
binder.updateXML(customer);

TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
t.transform(new DOMSource(document), new StreamResult(System.out));
}

}

バインダは、XML文書を新規作成するのでなく、元のDOMに変更を適用することによって、XML情報セット全体を保持します。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<customer>
<UNMAPPED_ELEMENT_1/>
<name>Jane Doe</name>
<!-- COMMENT #1 -->
<address>
<UNMAPPED_ELEMENT_2/>
<street>2 NEW STREET</street>
<!-- COMMENT #2 -->
<UNMAPPED_ELEMENT_3/>
<city>Any Town</city>
</address>
<!-- COMMENT #3 -->
<UNMAPPED_ELEMENT_4/>
<phone-number type="home">555-HOME</phone-number>
<!-- COMMENT #4 -->
<phone-number type="cell">555-CELL</phone-number>
<phone-number type="work">555-WORK</phone-number>
<UNMAPPED_ELEMENT_5/>
<!-- COMMENT #5 -->
</customer>

3 タイプ・レベルのマッピング

この章の内容は次のとおりです。

- デフォルトのルート要素の定義

- ネームスペース情報の設定

- 継承の指定

3.1 デフォルトのルート要素の定義

マップされたクラスのうち、少なくとも1つには、デフォルトのルート要素が定義されている必要があります。これにより、XML文書の最上位レベルのルートが何になるかがEclipseLinkに通知されます。図3-1に示すCustomerクラスとAddressクラスについて考えてみます。

図3-1 マップされたクラスのサンプル

これらのクラスは、例3-1に示すXMLスキーマに対応しています。このスキーマには、タイプcustomer-typeの最上位の要素が含まれているため、Customerクラスには、デフォルトのルート要素を指定する必要があります。

例3-1 XMLスキーマのサンプル

<xsd:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsd:complexType name="address-type">
<xsd:sequence>
<element name="street" type="xsd:string"/>
<element name="city" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>

<xsd:element name="customer" type="customer-type"/>

<xsd:complexType name="customer-type">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="billing-address" type="address-type"/>
<xsd:element name="shipping-address" type="address-type"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>

例3-2は、デフォルトのルート要素を指定するためにJavaクラスに注釈を付ける方法を示しています。必要なのは、JAXBの標準的な@XmlRootElement注釈のみです。

例3-2 @XmlRootElement注釈の使用

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement
public class Customer {
private String name;

@XmlElement(name="billing-address")
private Address billingAddress;

@XmlElement(name="shipping-address")
private Address shippingAddress;

...
}

例3-3は、EclipseLinkのOXMメタデータ形式でデフォルトのルート要素を指定する方法を示しています。

例3-3 デフォルトのルート要素の指定

...
<java-type name="Customer">
<xml-root-element/>
<java-attributes>
<xml-element java-attribute="name"/>
<xml-element java-attribute="billingAddress" name="billing-address"/>
<xml-element java-attribute="shippingAddress" name="shipping-address"/>
</java-attributes>
</java-type>
...

3.1.1 デフォルトのルート要素のカスタマイズ

例3-2では、クラスはCustomerと呼ばれ、XMLでのルート要素名はcustomerです。デフォルトでは、@XmlRootElementが指定されると、そのクラスの名前は先頭が小文字となり、ルート要素名として設定されます。ただし、XMLの要素名がJavaクラス名と異なる場合は、(例3-4に示すように)注釈に、または(例3-5に示すように)OXMメタデータに、name属性を含めることができます。

例3-4 注釈の使用

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement(name="my-customer")
public class Customer {
private String name;

@XmlElement(name="billing-address")
private Address billingAddress;

@XmlElement(name="shipping-address")
private Address shippingAddress;

...
}

例3-5 OXMメタデータの使用

...
<java-type name="Customer">
<xml-root-element name="my-customer"/>
<java-attributes>
<xml-element java-attribute="name"/>
<xml-element java-attribute="billingAddress" name="billing-address"/>
<xml-element java-attribute="shippingAddress" name="shipping-address"/>
</java-attributes>
</java-type>
...

JAXBの名前バインディング・アルゴリズムの詳細は、『Java Architecture for XML Binding (JAXB) Specification』(http://jcp.org/en/jsr/detail?id=222)の「Appendix D: Binding XML Names to Java Identifiers」を参照してください。

3.1.2 EclipseLinkがデフォルトのルート要素をどのように使用するかの理解

CustomerクラスのインスタンスをXMLに永続化する際には、EclipseLinkランタイムで次の処理が行われます。

- デフォルトのルート要素が取得されます。Customerクラス・インスタンスは、XML文書のルートに対応しています。EclipseLinkランタイムは、注釈とOXMのいずれかに指定されているデフォルトのルート要素(customer)を使用して、XML文書を開きます。次に、EclipseLinkはクラスに対するマッピングを使用して、そのオブジェクトの属性をマーシャリングします。

<customer>
<name>...</name>
</customer>

- EclipseLinkランタイムは、billingAddressなどのオブジェクト属性に遭遇すると、この属性に関連付けられているマッピングをチェックし、処理をどの要素(billing-address)に移すかを判別します。

<customer>
<name>...</name>
<billing-address/>
</customer>

- EclipseLinkランタイムはマッピングの参照ディスクリプタ(Address)をチェックして、永続化する属性を判別します。

<customer>
<name>...</name>
<billing-address>
<street>...</street>
<city>...</city>
</billing-address>
</customer>

3.2 ネームスペース情報の設定

ほとんどのXML文書は、ネームスペースで修飾されています。Javaクラスの要素は、次の各レベルでネームスペースによって修飾できます。

- パッケージ・レベルでの修飾

- タイプ・レベルでの修飾

- フィールド/プロパティ・レベルでの修飾

ほとんどの場合は、パッケージ・レベルの注釈で十分です。他のレベルを使用してドキュメントをカスタマイズできます。ネームスペースを指定するには、@XmlSchema注釈を使用します。

3.2.1 パッケージ・レベルでの修飾

パッケージに対して@XmlSchema注釈を使用して、デフォルトのネームスペースを設定し、パッケージ内の要素がすべてネームスペースで修飾されることを指定します。この情報は、特殊なJavaソース・ファイルpackage-info.javaで指定されています。

例3-6 注釈の使用

@XmlSchema(
namespace="http://www.example.org/package",
elementFormDefault=XmlNsForm.QUALIFIED)
package example;

import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;

これは、EclipseLink XMLバインディングで次のように指定できます。

例3-7 OXMメタデータの使用

<?xml version="1.0" encoding="UTF-8"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm">
<xml-schema
element-form-default="QUALIFIED"
namespace="http://www.example.org/package">
</xml-schema>

<java-types>
<java-type name="Customer">
...

</xml-bindings>

単純なCustomerクラスを使用すると、例3-6と例3-7では、次のXMLが生成されます。

<customer xmlns="http://www.example.org/package">
<name>Jane Doe</name>
<account>36328721</account>
</customer>

すべての要素は、http://www.example.org/packageネームスペースで修飾されています。

3.2.2 タイプ・レベルでの修飾

タイプ・レベルの注釈によって、パッケージ・レベルのネームスペースがオーバーライドされます。

例3-8 注釈の使用

package example;

@XmlRootElement
@XmlType(namespace="http://www.example.org/type")
public class Customer {
private String name;

private String account;

...
}

これは、EclipseLink XMLバインディングで次のように指定できます。

例3-9 XMLバインディング・ファイルの使用

<?xml version="1.0" encoding="UTF-8"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm">
<xml-schema
element-form-default="QUALIFIED"
namespace="http://www.example.org/package">
</xml-schema>

<java-types>
<java-type name="Customer">
<xml-type namespace="http://www.example.org/type" />
<java-attributes>
<xml-element java-attribute="name" />
<xml-element java-attribute="account" />
</java-attributes>
</java-type>
</java-types>
</xml-bindings>

これにより、次のXMLが生成されます。

<custom xmlns="http://www.example.org/package" xmlns:ns0="http://www.example.org/type">
<ns0:name>Bob</ns0:name>
<ns0:account>1928712</ns0:account>
</custom>

Customerタイプ内の要素は、http://www.example.org/typeネームスペースで修飾されています。

3.2.3 フィールド/プロパティ・レベルでの修飾

パッケージまたはタイプのネームスペースは、プロパティ/フィールド・レベルでオーバーライドできます。属性および要素の注釈はすべて、namespaceパラメータを受け入れます。

例3-10 ネームスペースのオーバーライド

package example;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(namespace="http://www.example.org/type")
public class Customer {
private String name;

@XmlElement(namespace="http://www.example.org/property")
private String account;

...
}

これは、EclipseLink XMLバインディングで次のように指定できます。

例3-11 バインディング・ファイルのサンプル

<?xml version="1.0" encoding="UTF-8"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm">
<xml-schema
element-form-default="QUALIFIED"
namespace="http://www.example.org/package">
</xml-schema>

<java-types>
<java-type name="Customer">
<xml-type namespace="http://www.example.org/type" />
<java-attributes>
<xml-element java-attribute="name" />
<xml-element java-attribute="account" namespace="http://www.example.org/property" />
</java-attributes>
</java-type>
</java-types>
</xml-bindings>

これにより、次のXMLが生成されます。

<custom xmlns="http://www.example.org/package" xmlns:ns1="http://www.example.org/property"
xmlns:ns0="http://www.example.org/type">
<ns0:name>Bob</ns0:name>
<ns1:account>1928712</ns1:account>
</custom>

account要素のみが、http://www.example.org/propertyネームスペースで修飾されています。

3.3 継承の指定

EclipseLink MOXyには、次のように、XMLでの継承階層を表す手段がいくつか用意されています。

- xsi:typeの使用

- 置換グループの使用

- @XmlDiscriminatorNode/@XmlDiscriminatorValueの使用

3.3.1 xsi:typeの使用

デフォルトでは、EclipseLinkは、継承をXMLで表すためにxsi:type属性を使用します。

この例では、抽象スーパー・クラス(ContactInfo)には、すべてのタイプの連絡先情報が含まれています。AddressとPhoneNumberは、ContactInfoの具体的な実装です。

例3-12 Javaクラスのサンプル

public abstract class ContactInfo {
}

public class Address extends ContactInfo {

private String street;
...

}

public class PhoneNumber extends ContactInfo {

private String number;
...

}

Customerオブジェクトには様々なタイプの連絡先情報を指定できるため、そのプロパティはスーパークラスを参照します。

@XmlRootElement
public class Customer {

private ContactInfo contactInfo;
...

}

例にあるCustomerをマーシャリングすると、次のXMLが生成されます。

<customer>
<contactInfo
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="address">
<street>323 Main Street</street>
</contactInfo>
</customer>

contactInfo要素上のxsi:type属性に注目してください。

3.3.2 置換グループの使用

XMLで継承をモデル化する別の方法は、XMLスキーマの置換グループ機能を使用することです。この手法を使用すると、要素名自体によって、どのサブクラスを使用するかが決定します。

同じ例3-12で、サブクラスのそれぞれに@XmlRootElement注釈を追加します。これは継承インジケータの役割を果たします。

例3-13 @XmlRootElement注釈の使用

public abstract class ContactInfo {
}

@XmlRootElement
public class Address extends ContactInfo {

private String street;
...

}

@XmlRootElement
public class PhoneNumber extends ContactInfo {

private String number;
...

}

この手法を使用し、例にあるCustomerをマーシャリングすると、次のXMLが生成されます。

<customer>
<address>
<street>323 Main Street</street>
</address>
</customer>

Addressオブジェクトがaddress要素にマーシャリングされていることに注意してください。

3.3.3 @XmlDiscriminatorNode/@XmlDiscriminatorValueの使用

また、(EclipseLink 2.2で導入された)MOXY固有の注釈である@XmlDiscriminatorNodeおよび@XmlDiscriminatorValueを使用して、継承を表すこともできます。この手法では、属性を選択してサブタイプを表すことができます。

例3-13を使用すると、ContactInfoクラスは@XmlDiscriminatorNode注釈を使用して、サブクラス・インジケータを保持するXML属性(classifier)を指定します。AddressとPhoneNumberに@XmlDiscriminatorValueという注釈が付けられ、そのクラスのインジケータ名(address-classifierとphone-number-classifier)が示されます。

例3-14 @XmlDiscriminatorNode注釈および@XmlDiscriminatorValue注釈の使用

@XmlDiscriminatorNode("@classifier")
public abstract class ContactInfo {
}

@XmlDiscriminatorValue("address-classifier")
public class Address extends ContactInfo {

private String street;
...

}

@XmlDiscriminatorValue("phone-number-classifier")
public class PhoneNumber extends ContactInfo {

private String number;
...

}

例3-14のXMLは、次のようになります。

<customer>
<contactInfo classifier="address-classifier">
<street>323 Main Street</street>
</contactInfo>
</customer>

AddressがcontactInfo要素にマーシャリングされていることに注目してください。そのclassifier属性には、識別子ノードの値address-classifierが含まれています。

4 単純な値のマッピング

この章の内容は次のとおりです。

- 単純な値のマッピング

- 列挙のマッピング

4.1 単純な値のマッピング

この項では、単純なJava値をXMLテキスト・ノードに直接マップする方法をいくつか示します。

- 属性へのマッピング

- テキスト・ノードへのマッピング

- 指定のスキーマ・タイプへのマッピング

- シンプル・タイプ・トランスレータによるマッピング

4.1.1 属性へのマッピング

例4-1のXMLスキーマであると仮定して、図4-1は、対応するXML文書内にある属性へのXMLダイレクト・マッピングを示します。

例4-1 XMLスキーマのサンプル

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:element name="customer" type="customer-type"/>

<xsd:complexType name="customer-type">
<xsd:attribute name="id" type="xsd:integer"/>
</xsd:complexType>

</xsd:schema>

図4-1 属性へのXMLダイレクト・マッピング

例4-2は、EclipseLinkでこのマッピングを取得するためにJavaクラスに注釈を付ける方法を示しています。必要なのは、JAXBの標準的な@XmlAttribute注釈のみです。

例4-2 @XmlAttribute注釈の使用

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlAttribute
private Integer id;

...
}

例4-3は、EclipseLinkのOXMメタデータ形式でマッピング情報を定義する方法を示しています。

例4-3 マッピングの定義

...
<java-type name="Customer">
<xml-root-element name="customer"/>
<java-attributes>
<xml-attribute java-attribute="id"/>
</java-attributes>
</java-type>
...

4.1.2 テキスト・ノードへのマッピング

この項では、次を実行した場合のXMLダイレクト・マッピングの使用方法について説明します。

- 単純な順序でのテキスト・ノードへのマッピング

- サブ要素でのテキスト・ノードへのマッピング

- 位置によるテキスト・ノードへのマッピング

- 単純なテキスト・ノードへのマッピング

4.1.2.1 単純な順序でのテキスト・ノードへのマッピング

例4-4のXMLスキーマであると仮定して、図4-2は、対応するXML文書内にある順序での個々のテキスト・ノードへのXMLダイレクト・マッピングを示します。

例4-4 XMLスキーマのサンプル

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:element name="customer" type="customer-type"/>

<xsd:complexType name="customer-type">
<xsd:sequence>
<xsd:element name="first-name" type="xsd:string"/>
<xsd:element name="last-name" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>

</xsd:schema>

図4-2 個々のテキスト・ノードへのXMLダイレクト・マッピング

例4-1は、EclipseLinkでこのマッピングを取得するためにJavaクラスに注釈を付ける方法を示しています。ここでは、JAXBの標準的な@XmlElement注釈が、カスタマイズされた要素名とともに使用されます。

例4-5 @XmlElement注釈の使用

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlElement(name="first-name")
private String firstName;

@XmlElement(name="last-name")
private String lastName;

...
}

例4-5では、マップされた属性に対してXML要素名を明示的に指定しました。これは省略可能な構成であり、名前を明示的に設定しなくても、XML要素はJava属性名を照合するのみで、XMLでは<firstName>Jane</firstName>のようになります。JAXBの名前バインディング・アルゴリズムの詳細は、『Java Architecture for XML Binding (JAXB) Specification』(http://jcp.org/en/jsr/detail?id=222)の「Appendix D: Binding XML Names to Java Identifiers」を参照してください。

例4-6は、EclipseLinkのOXMメタデータ形式でマッピング情報を定義する方法を示しています。カスタム要素のnameを指定するには、name属性を使用します。

例4-6 name属性の使用

...
<java-type name="Customer">
<xml-root-element name="customer"/>
<java-attributes>
<xml-element java-attribute="firstName" name="first-name"/>
<xml-element java-attribute="lastName" name="last-name"/>
</java-attributes>
</java-type>
...

4.1.2.2 サブ要素でのテキスト・ノードへのマッピング

例4-7のXMLスキーマであると仮定して、図4-3は、対応するXML文書内のサブ要素にあるテキスト・ノードへのXMLダイレクト・マッピングを示します。

例4-7 XMLスキーマのサンプル

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:element name="customer" type="customer-type"/>

<xsd:complexType name="customer-type">
<xsd:sequence>
<xsd:element name="personal-info">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="first-name" type="xsd:string"/>
<xsd:element name="last-name" type="xsd:string"/>
<xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>

</xsd:schema>

図4-3 サブノードでのテキスト要素へのXMLダイレクト・マッピング

例4-8は、EclipseLinkでこのマッピングを取得するためにJavaクラスに注釈を付ける方法を示しています。ここでは、単純な要素名のカスタマイズではなく、新しいXML構造を実際に導入しているので、EclipseLinkの@XmlPath注釈が使用されています。

例4-8 @XmlPath注釈の使用

package example;

import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlPath("personal-info/first-name/text()")
private String firstName;

@XmlPath("personal-info/last-name/text()")
private String lastName;

...
}

例4-9は、EclipseLinkのOXMメタデータ形式でマッピング情報を定義する方法を示しています。ここでは、カスタマイズされたXMLパスがxml-path属性に定義されます。

例4-9 xml-path属性の使用

...
<java-type name="Customer">
<xml-root-element name="customer"/>
<java-attributes>
<xml-element java-attribute="firstName" xml-path="personal-info/first-name/text()"/>
<xml-element java-attribute="lastName" xml-path="personal-info/last-name/text()"/>
</java-attributes>
</java-type>
...

4.1.2.3 位置によるテキスト・ノードへのマッピング

例4-10のXMLスキーマであると仮定して、図4-4は、対応するXML文書の位置によるテキスト・ノードへのXMLダイレクト・マッピングを示します。

例4-10 XMLスキーマのサンプル

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:element name="customer" type="customer-type"/>

<xsd:complexType name="customer-type">
<xsd:sequence>
<xsd:element name="name" type="xsd:string" maxOccurs="2"/>
</xsd:sequence>
</xsd:complexType>

</xsd:schema>

図4-4 テキスト・ノードへのXMLダイレクト・マッピング

例4-11は、Javaでのこのマッピングの構成方法を示しています。再び、より複雑なXMLパスのカスタマイズに向けて、EclipseLinkの@XmlPath注釈が使用されています。

例4-11 @XmlPath注釈の使用

package example;

import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlPath("name[1]/text()")
private String firstName;

@XmlPath("name[2]/text()")
private String lastName;

...
}

例4-12は、EclipseLinkのOXMメタデータ形式でマッピング情報を定義する方法を示しています。

例4-12 マッピングの定義

...
<java-type name="Customer">
<xml-root-element name="customer"/>
<java-attributes>
<xml-element java-attribute="firstName" xml-path="name[1]/text()"/>
<xml-element java-attribute="lastName" xml-path="name[2]/text()"/>
</java-attributes>
</java-type>
...

4.1.2.4 単純なテキスト・ノードへのマッピング

例4-13のXMLスキーマであると仮定して、図4-5は、対応するXML文書内にある単純なテキスト・ノードへのXMLダイレクト・マッピングを示します。

例4-13 XMLスキーマのサンプル

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:element name="phone-number" type="xsd:string"/>

</xsd:schema>

図4-5 単純なテキスト・ノードへのXMLダイレクト・マッピング

例4-14は、EclipseLinkでこのマッピングを取得するためにJavaクラスに注釈を付ける方法を示しています。この場合は、@XmlValue注釈が使用されます。

例4-14 @XmlValue注釈の使用

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement(name="phone-number")
@XmlAccessorType(XmlAccessType.FIELD)
public class PhoneNumber {
@XmlValue
private String number;

...
}

例4-15は、EclipseLinkのOXMメタデータ形式でマッピング情報を定義する方法を示しています。

例4-15 マッピングの定義

...
<java-type name="PhoneNumber">
<xml-root-element name="phone-number"/>
<java-attributes>
<xml-value java-attribute="number"/>
</java-attributes>
</java-type>
...

4.1.3 指定のスキーマ・タイプへのマッピング

ほとんどの場合、EclipseLinkではXML文書のターゲット書式を決定できます。ただし、EclipseLinkで使用可能な複数のターゲットから、1つを指定する必要がある場合があります。たとえば、java.util.Calendarは、スキーマdate、time、dateTimeのノードのいずれかにマーシャリングできる場合があり、byte[]は、スキーマhexBinaryまたはbase64Binaryノードにマーシャリングできる場合があります。

例4-16と図4-6のXMLスキーマは、JavaのCalendarオブジェクトからXMLのdateフィールドへのXMLダイレクト・マッピングを示します。

例4-16 XMLスキーマのサンプル

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:element name="customer" type="customer-type"/>

<xsd:complexType name="customer-type">
<xsd:sequence>
<xsd:element name="hire-date" type="xsd:date"/>
</xsd:sequence>
</xsd:complexType>

</xsd:schema>

図4-6 特定のスキーマ・タイプへのXMLダイレクト・マッピング

例4-17は、EclipseLinkでこのマッピングを取得するためにJavaクラスに注釈を付ける方法を示しています。ここでは、マーシャリングされたXMLに出現するデータ型を指定するために、@XmlSchemaTypeが使用されています。

例4-17 @XmlSchemaType注釈の使用

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlElement(name="hire-date")
@XmlSchemaType(name="date")
private Calendar hireDate;

...
}

例4-18は、EclipseLinkのOXMメタデータ形式でマッピング情報を定義する方法を示しています。

例4-18 マッピングの定義

...
<java-type name="Customer">
<xml-root-element name="customer"/>
<java-attributes>
<xml-element java-attribute="hireDate" name="hire-date">
<xml-schema-type name="date"/>
</xml-element>
</java-attributes>
</java-type>
...

4.1.3.1 Javaタイプ・アダプタの使用

例4-19と図4-7は、別々のバイナリ型の2つの異なるテキスト・ノードへのXMLダイレクト・マッピングを示しています。

例4-19 XMLスキーマのサンプル

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:element name="customer" type="customer-type"/>

<xsd:complexType name="customer-type">
<xsd:sequence>
<xsd:element name="resume" type="xsd:base64Binary"/>
<xsd:element name="picture" type="xsd:hexBinary"/>
</xsd:sequence>
</xsd:complexType>

</xsd:schema>

図4-7 別々のバイナリ型の異なるテキスト・ノードへのXMLダイレクト・マッピング

例4-20は、EclipseLinkでこのマッピングを取得するためにJavaクラスに注釈を付ける方法を示しています。デフォルトでは、JAXBによってbyte[]がbase64Binaryにマーシャリングされるので、再開マッピングに必要となるものは特にありません。hexBinaryフィールドにマップするには、@XmlSchemaType注釈でXMLタイプを指定する一方、@XmlJavaTypeAdapterでは、値への変換を担当するアダプタ・クラス(この場合は組込みJAXB HexBinaryAdapter)を指定します。

例4-20 @XmlSchemaType注釈および@XmlJavaTypeAdapter注釈の使用

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
private byte[] resume;

@XmlSchemaType(name="hexBinary")
@XmlJavaTypeAdapter(HexBinaryAdapter.class)
private byte[] picture;

...
}

例4-21は、EclipseLinkのOXMメタデータ形式でマッピング情報を定義する方法を示しています。

例4-21 マッピングの定義

...
<java-type name="Customer">
<xml-root-element name="customer"/>
<java-attributes>
<xml-element java-attribute="resume"/>
<xml-element java-attribute="picture">
<xml-schema-type name="hexBinary"/>
<xml-java-type-adapter value="javax.xml.bind.annotation.adapters.HexBinaryAdapter"/>
</xml-element>
</java-attributes>
</java-type>
...

4.1.4 シンプル・タイプ・トランスレータによるマッピング

ノードのタイプをXMLスキーマで定義していない場合は、XMLダイレクト・マッピングを構成してxsi:type属性を使用し、タイプ情報を提供できます。

例4-22のXMLスキーマであると仮定して、図4-8は、対応するXML文書にマップできるJavaクラスを示します。

例4-22 XMLスキーマのサンプル

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:element name="phone-number" type="phone-number-type"/>

<xsd:complexType name="phone-number-type">
<xsd:sequence>
<xsd:element name="area-code" type="anySimpleType"/>
<xsd:element name="number" type="anySimpleType"/>
</xsd:sequence>
</xsd:complexType>

</xsd:schema>

図4-8 Javaクラスのサンプル

図4-9は、XML文書のシンプル・タイプ・トランスレータによるXMLダイレクト・マッピングを示します。このXML文書は前述のスキーマに準拠しています。

図4-9 シンプル・タイプ・トランスレータによるXMLダイレクト・マッピング

例4-23は、EclipseLinkでこのマッピングを取得するためにJavaクラスに注釈を付ける方法を示しています。

例4-23 マッピングのサンプル

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement(name="phone-number")
public class PhoneNumber {
@XmlElement(name="area-code")
private Object areaCode;

private Object number;

...
}

例4-24は、EclipseLinkのOXMメタデータ形式でマッピング情報を定義する方法を示しています。

例4-24 XMLマッピングのサンプル

...
<java-type name="PhoneNumber">
<xml-root-element name="phone-number"/>
<java-attributes>
<xml-element java-attribute="areaCode" name="area-code"/>
<xml-element java-attribute="number"/>
</java-attributes>
</java-type>
...

4.2 単純な値のコレクションのマッピング

この項では、単純なJava値のコレクションをXMLテキスト・ノードに直接マップする方法をいくつか示します。

- テキスト・ノードへのマッピング

- グループ化要素によるテキスト・ノードへのマッピング

- リスト要素へのマッピング

- XmlAttributesまたはXmlValuesのコレクションのマッピング

4.2.1 テキスト・ノードへのマッピング

例4-25のXMLスキーマであると仮定して、図4-10では、対応するXML文書内にある要素へのJavaコレクションのマッピングを示します。

例4-25 XMLスキーマのサンプル

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:element name="customer" type="customer-type"/>

<xsd:complexType name="customer-type">
<xsd:sequence>
<xsd:element name="email-address" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>

</xsd:schema>

図4-10 テキスト・ノードへのXMLダイレクト・コレクション・マッピング

例4-26は、EclipseLinkでこのマッピングを取得するためにJavaクラスに注釈を付ける方法を示しています。必要なのは、JAXBの標準的な@XmlElement注釈のみです。

例4-26 @XmlElement注釈の使用

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlElement(name="email-address")
private List<String> emailAddress;

...
}

例4-27は、EclipseLinkのOXMメタデータ形式でマッピング情報を定義する方法を示しています。

例4-27 XMLマッピングのサンプル

...
<java-type name="Customer">
<xml-root-element name="customer"/>
<java-attributes>
<xml-element java-attribute="emailAddresses" name="email-address"/>
</java-attributes>
</java-type>
...

4.2.2 グループ化要素によるテキスト・ノードへのマッピング

例4-28のXMLスキーマであると仮定して、図4-11では、対応するXML文書内にある要素へのJavaコレクションのマッピングを示します。ここでは、コレクションの要素を整理するためにグループ化要素を使用しています。

例4-28 XMLスキーマのサンプル

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:element name="customer" type="customer-type"/>

<xsd:complexType name="customer-type">
<xsd:sequence>
<xsd:element name="email-address" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>

</xsd:schema>

図4-11 グループ化要素によるテキスト・ノードへのXMLダイレクト・コレクション・マッピング

例4-29は、EclipseLinkでこのマッピングを取得するためにJavaクラスに注釈を付ける方法を示しています。グループ化要素は、@XmlElementWrapper注釈を使用して指定します。

例4-29 @XmlElementWrapper注釈の使用

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlElement(name="email-address")
@XmlElementWrapper(name="email-addresses")
private List<String> emailAddresses;

...
}

例4-30は、EclipseLinkのOXMメタデータ形式でマッピング情報を定義する方法を示しています。

例4-30 XMLマッピングのサンプル

...
<java-type name="Customer">
<xml-root-element name="customer"/>
<java-attributes>
<xml-element java-attribute="emailAddresses" name="email-address">
<xml-element-wrapper name="email-addresses"/>
</xml-element>
</java-attributes>
</java-type>
...

4.2.3 リスト要素へのマッピング

例4-31のXMLスキーマであると仮定して、図4-12では、対応するXML文書内にあるxsd:listタイプへのマッピングを示します。このマッピングを使用すると、単純なJavaオブジェクトのコレクションを、XML内で空白で区切られたトークンのStringとして表すことができます。ここで、タスク・リストには、Design、Code、Testの3つのエントリが含まれています。

例4-31 XMLスキーマのサンプル

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:element name="employee" type="employee-type"/>

<xsd:complexType name="employee-type">
<xsd:sequence>
<xsd:element name="tasks" type="tasks-type"/>
</xsd:sequence>
</xsd:complexType>

<xsd:simpleType name="tasks-type">
<xsd:list itemType="xsd:string"/>
</xsd:simpleType>

</xsd:schema>

図4-12 listフィールドへのXMLダイレクト・マッピング

例4-32は、EclipseLinkでこのマッピングを取得するためにJavaクラスに注釈を付ける方法を示しています。

例4-32 @XmlList注釈の使用

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Employee {
@XmlList
private List<String> tasks;

...
}

例4-33は、EclipseLinkのOXMメタデータ形式でマッピング情報を定義する方法を示しています。

例4-33 XMLマッピングのサンプル

...
<java-type name="Employee">
<xml-root-element name="employee"/>
<java-attributes>
<xml-element java-attribute="tasks" xml-list="true"/>
</java-attributes>
</java-type>
...

4.2.4 XmlAttributesまたはXmlValuesのコレクションのマッピング

例4-34に示すように、@XmlListは、@XmlAttributeまたは@XmlValueと組み合せて使用することもできます。このコレクションは、属性内のスペース区切りの文字列として表されます。

例4-34 Javaの注釈

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlAttribute
@XmlList
private List<Integer> ids;

...
}

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement(name="phone-numbers")
@XmlAccessorType(XmlAccessType.FIELD)
public class PhoneNumbers {
@XmlValue
@XmlList
private List<String> numbers;

...
}

例4-35 EclipseLink OXMメタデータ

...
<java-type name="Customer">
<xml-root-element name="customer"/>
<java-attributes>
<xml-attribute java-attribute="ids" xml-list="true"/>
</java-attributes>
</java-type>
...

...
<java-type name="PhoneNumbers">
<xml-root-element name="phone-numbers"/>
<java-attributes>
<xml-value java-attribute="numbers" xml-list="true"/>
</java-attributes>
</java-type>
...

例4-36 XML文書の例

<customer ids="726 1982 1989 2991"/>

<phone-numbers>6132883982 6139828817 18882982298</phone-numbers>

4.3 単一のプロパティに対する複数のマッピング

標準的なJAXBでは、Javaフィールドごとに指定できるマッピングはせいぜい1つです。EclipseLink MOXy 2.3以降では、OXMメタデータを使用して1つのプロパティに複数のマッピングを作成できます。ただし、読取り可能なマッピングはせいぜい1つです(それ以外は「書込み専用」です)。

4.3.1 例

この例では、次のJavaクラスを使用します。

例4-37 Javaクラスのサンプル

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class CustomQuoteRequest {

private int requestId;

private String currencyPairCode;

}

例4-38は、EclipseLinkのOXMメタデータ形式でcurrencyPairCodeの複数のマッピングを定義する方法を示しています。2番目のマッピングではwrite-only="true"と指定されていることに注意してください。

例4-38 XMLスキーマのサンプル

...
<java-type name="CustomQuoteRequest">
<xml-root-element/>
<java-attributes>
<xml-element java-attribute="requestId" name="id"/>
<xml-attribute java-attribute="currencyPairCode" xml-path="req/info/instrmt/@sym"/>
<xml-attribute java-attribute="currencyPairCode" xml-path="req/info/leg/token/@sym" write-only="true"/>
</java-attributes>
</java-type>
...

4.3.2 XML出力

例4-39に、CustomQuoteRequestがXMLにマーシャリングされる例を示します。

例4-39 結果となるXML

<?xml version="1.0" encoding="UTF-8"?>
<customQuoteRequest>
<id>881</id>
<req>
<info>
<instrmt sym="CAD/USD"/>
<leg>
<token sym="CAD/USD"/>
</leg>
</info>
</req>
</customQuoteRequest>

4.4 列挙のマッピング

Java列挙は、@XmlEnum注釈と@XmlEnumValue注釈を使用してXMLにマップできます。

4.4.1 定数名を使用した列挙のマッピング

次のスキーマは、XML列挙を示しています。

例4-40 XMLスキーマのサンプル

<xs:simpleType name="CustomerType">
<xs:restriction base="xs:string"/>
<xs:enumeration value="PROMO_CUSTOMER"/>
<xs:enumeration value="NEW_CUSTOMER"/>
<xs:enumeration value="VIP"/>
<xs:enumeration value="NORMAL"/>
</xs:restriction>
</xs:simpleType>

enum定数名自体がXML表現に十分な場合は、@XmlEnum注釈を使用するだけです。

例4-41 @XmlEnum注釈の使用

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
private CustomerType type = CustomerType.NEW_CUSTOMER;

@XmlEnum(String.class)
private enum CustomerType { PROMO_CUSTOMER, NEW_CUSTOMER, VIP, NORMAL }

...
}

例4-42は、EclipseLinkのOXMメタデータでこのマッピングを定義する方法を示しています。

例4-42 XMLマッピングのサンプル

...
<xml-enums>
<xml-enum java-enum="CustomerType" value="java.lang.String">
<xml-enum-value java-enum-value="PROMO_CUSTOMER">PROMO_CUSTOMER</xml-enum-value>
<xml-enum-value java-enum-value="NEW_CUSTOMER">NEW_CUSTOMER</xml-enum-value>
<xml-enum-value java-enum-value="VIP">VIP</xml-enum-value>
<xml-enum-value java-enum-value="NORMAL">NORMAL</xml-enum-value>
</xml-enum>
</xml-enums>

<java-types>
<java-type name="Customer">
<xml-root-element name="customer"/>
<java-attributes>
<xml-element java-attribute="type" type="CustomerType"/>
</java-attributes>
</java-type>
</java-types>
...

XMLにマーシャリングすると、Customerは次のようになります。

<customer>
<type>NEW_CUSTOMER</type>
</customer>

4.4.2 カスタムXML値への列挙のマッピング

次のXMLスキーマ列挙が与えられたとします。

例4-43 XMLスキーマのサンプル

<xs:simpleType name="CustomerType">
<xs:restriction base="xs:int">
<xs:enumeration value="1"/>
<xs:enumeration value="2"/>
<xs:enumeration value="3"/>
<xs:enumeration value="4"/>
</xs:restriction>
</xs:simpleType>

これをJavaでモデル化するには、enum定数ごとに、XML値を提供するために@XmlEnumValue注釈が使用されます。

例4-44 @XmlEnumValue注釈の使用

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {

private CustomerType type = CustomerType.NEW_CUSTOMER;

@XmlEnum(Integer.class)
private enum CustomerType {
@XmlEnumValue("1") PROMO_CUSTOMER,
@XmlEnumValue("2") NEW_CUSTOMER,
@XmlEnumValue("3") VIP,
@XmlEnumValue("4") NORMAL;

...
}

例4-45は、EclipseLinkのOXMメタデータでこのマッピングを定義する方法を示しています。

例4-45 XMLマッピングのサンプル

...
<xml-enums>
<xml-enum java-enum="CustomerType" value="java.lang.Integer">
<xml-enum-value java-enum-value="PROMO_CUSTOMER">1</xml-enum-value>
<xml-enum-value java-enum-value="NEW_CUSTOMER">2</xml-enum-value>
<xml-enum-value java-enum-value="VIP">3</xml-enum-value>
<xml-enum-value java-enum-value="NORMAL">4</xml-enum-value>
</xml-enum>
</xml-enums>

<java-types>
<java-type name="Customer">
<xml-root-element name="customer"/>
<java-attributes>
<xml-element java-attribute="type" type="CustomerType"/>
</java-attributes>
</java-type>
</java-types>
...

XMLにマーシャリングすると、新しいCustomerは次のようになります。

<customer>
<type>2</type>
</customer>

5 特殊なスキーマ・タイプのマッピング

この章の内容は次のとおりです。

- 日付および時刻のマッピング

- unionフィールドへのマッピング

- バイナリ型

5.1 日付および時刻のマッピング

@XmlSchemaType注釈を使用して、日付および時刻の情報のXML表示をカスタマイズできます。また、EclipseLink MOXyでは、JAXB仕様(JSR-222)で扱われていない次の型がサポートされています。

- java.sql.Date

- java.sql.Time

- java.sql.Timestamp

次のXMLスキーマには、xsd:date型のdate-of-birth要素が含まれています。

例5-1 XMLスキーマのサンプル

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="customer">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="date-of-birth" type="xsd:date" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>

5.1.1 生成されたモデルの理解

JAXB XML Schema to Javaコンパイラ(XJC)を使用して、サンプル・スキーマからクラス・モデルを生成できます。次に例を示します。

> xjc -d output-dir -p example date.xsd

これにより、次のCustomerクラスが生成されます。

例5-2 Customerクラスのサンプル

package example;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
import javax.xml.datatype.XMLGregorianCalendar;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {"dateOfBirth"})
@XmlRootElement(name = "customer")
public class Customer {

@XmlElement(name = "date-of-birth")
@XmlSchemaType(name = "date")
protected XMLGregorianCalendar dateOfBirth;

public XMLGregorianCalendar getDateOfBirth() {
return dateOfBirth;
}

public void setDateOfBirth(XMLGregorianCalendar value) {
this.dateOfBirth = value;
}

}

次の点に注意してください。

- dateOfBirthプロパティの型はjavax.xml.datatype.XMLGregorianCalendar

- dateOfBirthプロパティは@XmlSchemaType注釈を使用

Javaの一部のデータ型(XMLGregorianCalendarなど)には、複数のXML表現(xsd:date、xsd:time、xsd:dateTimeなど)があります。@XmlSchemaTypeを使用して適切な表現を選択します。

5.1.2 異なるDate(またはCalendar)プロパティの使用

デフォルトでは、JAXB XML schema to Javaコンパイラ(XJC)によって、XMLGregorianCalendar型のプロパティが生成されます。ただし、例5-3に示すように、これは、java.util.Dateまたはjava.util.Calendarに簡単に変更できます。

例5-3 java.util.Dateの使用

package blog.date;

import java.util.Date;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchemaType;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "customer")
public class Customer {

@XmlElement(name = "date-of-birth")
@XmlSchemaType(name = "date")
protected Date dateOfBirth;

public Date getDateOfBirth() {
return dateOfBirth;
}

public void setDateOfBirth(Date value) {
this.dateOfBirth = value;
}

}

5.2 unionフィールドへのマッピング

次のXMLスキーマとクラス図は、XMLスキーマの結合の代表的な用途を示しています。

例5-4 XMLスキーマの結合

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="customer" type="customer-type" />
<xsd:complexType name="customer-type">
<xsd:sequence>
<xsd:element name="shoe-size" type="size-type" />
</xsd:sequence>
</xsd:complexType>
<xsd:simpleType name="size-type">
<xsd:union memberTypes="xsd:decimal xsd:string" />
</xsd:simpleType>
</xsd:schema>

図5-1 unionフィールドへのマッピング

図5-2に、XML文書内のunionフィールドへのマッピングを示します。このXML文書はこの例のスキーマに準拠しています。EclipseLinkでXML文書をアンマーシャリングする場合、正常な変換が行われるまで、union型のそれぞれが試行されます。unionにおける最初のスキーマ・タイプはxsd:decimalです。10.5は有効な10進数であるため、EclipseLinkはその値を適切な型に変換します。

図5-2 XML文書内にあるunionフィールドへのマッピング

図5-3では、値Mは、有効なxsd:decimal型ではないため、次のunion型であるxsd:stringが試行されます。

図5-3 unionフィールドへのマッピング

現時点では、EclipseLinkは、注釈またはOXMメタデータを使用したunionのマッピングをサポートしていません。ただし、EclipseLinkのXMLカスタマイザを使用してマッピングを作成することはできます。

最初に、shoeSize属性に@XmlTransientという注釈を付けて、そのマッピングが自動的に生成されないようにします。また、@XmlCustomizer注釈も付けます。こうすると、CustomerCustomizerクラスによって、コード内にunionマッピングが作成されます。

例5-5 EclipseLinkのカスタマイザの使用

package example;

import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlCustomizer(CustomerCustomizer.class)
public class Customer {
@XmlTransient
private Object shoeSize;

...
}

CustomerCustomizerクラスを使用すると、shoeSize属性にマッピングを手動で追加できます。例5-6では、マッピング上にXMLUnionFieldが構成されており、addSchemaType()を呼び出すことによって、可能なunionメンバー・タイプが追加されます。

例5-6 unionフィールドのマッピング

package example;

import org.eclipse.persistence.config.DescriptorCustomizer;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.oxm.*;

public class CustomerCustomizer implements DescriptorCustomizer {

@Override
public void customize(ClassDescriptor descriptor) throws Exception {
XMLDirectMapping shoeSizeMapping = new XMLDirectMapping();
shoeSizeMapping.setAttributeName("shoeSize");

XMLUnionField shoeSizeField = new XMLUnionField();
shoeSizeField.setXPath("shoe-size/text()");
shoeSizeField.addSchemaType(XMLConstants.DECIMAL_QNAME);
shoeSizeField.addSchemaType(XMLConstants.STRING_QNAME);

shoeSizeMapping.setField(shoeSizeField);

descriptor.addMapping(shoeSizeMapping);
}

}

5.2.1 変換順の理解

addSchemaType()の呼出し順は重要です。XMLの値をJavaに変換する際、EclipseLinkは、フィールドに追加された順に変換を試行し、正常な変換が行われるとすぐに戻ります。たとえば、10.5のshoeSizeをアンマーシャリングするとします。

...
shoeSizeField.addSchemaType(XMLConstants.DECIMAL_QNAME);
shoeSizeField.addSchemaType(XMLConstants.STRING_QNAME);
...

BigDecimalが作成され、値が格納されます。ただし、XMLUnionFieldが次のように設定された場合は異なります。

...
shoeSizeField.addSchemaType(XMLConstants.STRING_QNAME);
shoeSizeField.addSchemaType(XMLConstants.DECIMAL_QNAME);
...

この場合は、shoeSizeの値はString("10.5")となります。

5.2.2 変換クラスのカスタマイズ

EclipseLinkでは、一連のデフォルトの変換を使用して、Java属性の値を作成します(この場合は、xsd:decimalがBigDecimalに変換されます)。XMLUnionFieldメソッドaddConversionを使用して、Javaコードにおけるこの動作をオーバーライドできます。たとえば、JavaオブジェクトによってshoeSizeをFloatとして格納する場合は、次のようにします。

shoeSizeField.addConversion(XMLConstants.DECIMAL_QNAME, Float.class);

5.3 バイナリ型

byte[]やByte[]などのバイナリ型フィールドにマッピングする際には、これ以外にも検討すべき項目があります。

5.3.1 バイナリのBase64形式およびHex形式の指定

EclipseLinkでは、base64Binary(デフォルト)とhexBinaryという、2つの表現形式でのバイナリ・データのマーシャリングおよびアンマーシャリングをサポートしています。EclipseLink OXMで@XmlSchemaType注釈または<xml-schema-type>要素を使用して、目的のバイナリ形式を指定できます。次の例は、これらの形式のそれぞれに対して同じbyte[]をマーシャリングした結果を示しています。

例5-7 注釈

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement
public class BinaryData {

@XmlSchemaType(name="hexBinary")
public byte[] hexBytes;

@XmlSchemaType(name="base64Binary")
public byte[] base64Bytes;

}

例5-8 EclipseLink OXM

...
<java-type name="example.BinaryData">
<xml-root-element/>
<java-attributes>
<xml-element java-attribute="hexBytes">
<xml-schema-type name="hexBinary"/>
</xml-element>
<xml-element java-attribute="base64Bytes">
<xml-schema-type name="base64Binary"/>
</xml-element>
</java-attributes>
</java-type>
...

BinaryData b = new BinaryData();
b.hexBytes = new byte[] {2,4,8,16,32,64};
b.base64Bytes = b.hexBytes;

jaxbContext.createMarshaller().marshal(b, System.out);

例5-9 出力

<?xml version="1.0" encoding="UTF-8"?>
<binaryData>
<hexBytes>020308102040</hexBytes>
<base64Bytes>AgMIECBA</base64Bytes>
</binaryData>

5.3.2 byte[]とByte[]の比較の理解

他のJavaプリミティブ/ラッパー型と異なり、EclipseLinkでは、byte[](プリミティブ)データ型とByte[](ラッパー)データ型が区別されます。次の例で示されているように、デフォルトでは、byte[]は単一の要素または属性にマーシャリングする一方、Byte[]はそれぞれのbyteをその要素としてマーシャリングします。

例5-10 byte[]とByte[]の使用

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement
public class BinaryData {

public byte[] primitiveBytes;
public Byte[] byteObjects;

}

BinaryData b = new BinaryData();
b.primitiveBytes = new byte[] {34,45,56,67,78,89,89,34,23,12,12,11,2};
b.byteObjects = new Byte[] {23,1,112,12,1,64,1,14,3,2};

jaxbContext.createMarshaller().marshal(b, System.out);

例5-11 出力

<?xml version="1.0" encoding="UTF-8"?>
<binaryData>
<primitiveBytes>Ii04Q05ZWSIXDAwLAg==</primitiveBytes>
<byteObjects>23</byteObjects>
<byteObjects>1</byteObjects>
<byteObjects>112</byteObjects>
<byteObjects>12</byteObjects>
<byteObjects>1</byteObjects>
<byteObjects>64</byteObjects>
<byteObjects>1</byteObjects>
<byteObjects>14</byteObjects>
<byteObjects>3</byteObjects>
<byteObjects>2</byteObjects>
</binaryData>

5.3.3 SOAP添付ファイルの使用

Webサービス環境でEclipseLink MOXyを使用している場合は、一部のタイプのバイナリ・データは、XMLの要素または属性に直接書き込まれるのではなく、MTOM/XOP添付ファイルとして作成されることがあります。これは、大量のバイナリ・データの最適化として行われます。

次の表は、添付ファイルとして自動的に扱われるJavaタイプを、それに対応するMIMEタイプとともに示しています。

表5-1 添付ファイルとして扱われるJava属性

Javaタイプ

MIMEタイプ

java.awt.Image

image/gif

java.awt.Image

image/jpeg

javax.xml.transform.Source

text/xml

application/xml

*

javax.activation.DataHandler

*/*

注意:
SOAP添付ファイルの基本の詳細は、『Java Architecture for XML Binding (JAXB) Specification』(http://jcp.org/en/jsr/detail?id=222)の「Appendix H: Enhanced Binary Data Handling」を参照してください。

次のJavaクラスには、単純なbyte[]とjava.awt.Imageという2つのバイナリ・フィールドがあります。Webサービス環境では、Imageデータは添付ファイルとして自動的に作成されます。

例5-12 Javaクラスのサンプル

package example;

import java.awt.Image;

import javax.xml.bind.annotation.*;

@XmlRootElement
public class BinaryData {

public byte[] bytes;

public Image photo;

}

Webサービス環境では、例5-12に示すオブジェクトのマーシャリングは例5-13のようになります(実際の外観は、アプリケーション・サーバーのAttachmentMarshallerの実装に応じて異なります)。

例5-13 結果となるXML

<?xml version="1.0" encoding="UTF-8"?>
<binaryData>
<bytes>Ii04Q05ZWSIXDAwLAg==</bytes>
<photo>
<xop:Include href="cid:1" xmlns:xop="http://www.w3.org/2004/08/xop/include"/>
</photo>
</binaryData>

5.3.3.1 @XmlInlineBinaryDataの使用

バイナリ・データがインラインのstringとしてXMLに強制的に書き込まれるようにする場合は、フィールドに@XmlInlineBinaryDataという注釈を付けることができます。

例5-14 @XmlInlineBinaryData注釈の使用

package example;

import java.awt.Image;

import javax.xml.bind.annotation.*;

@XmlRootElement
public class BinaryData {

public byte[] bytes;

@XmlInlineBinaryData
public Image photo;

}

これで、XML文書は次のようになります。

<?xml version="1.0" encoding="UTF-8"?>
<binaryData>
<bytes>Ii04Q05ZWSIXDAwLAg==</bytes>
<photo>/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHB ... Af/2Q==</photo>
</binaryData>

5.3.3.2 @XmlMimeTypeの使用

@XmlMimeType注釈を使用して、バイナリ・フィールドのMIMEタイプを明示的に設定できます。アプリケーションのAttachmentMarshallerとAttachmentUnmarshallerは、この情報の処理を担当します。

例5-15 @XmlMimeType注釈の使用

package example;

import java.awt.Image;

import javax.xml.bind.annotation.*;

@XmlRootElement
public class BinaryData {

public byte[] bytes;

@XmlMimeType("image/gif")
public Image photo;

}

6 私有のリレーションシップ

この章の内容は次のとおりです。

- 私有の1対1リレーションシップのマッピング

- 私有の1対多リレーションシップのマッピング

6.1 私有の1対1リレーションシップのマッピング

この項では、オブジェクト間の1対1リレーションシップをマップする方法をいくつか示します。デフォルトでは、1対1リレーションシップはJAXB内で私有であり、そのXMLコンテンツは、所有側の要素の内側にネストされて表示されます。たとえば、PhoneNumberに1対1マッピングされているCustomerは、次のようにマーシャリングされます。

例6-1 XMLマッピングのサンプル

<customer>
<name>Bob Smith</name>
<id>1982812</id>
<phone-number>
<area-code>613</area-code>
<number>5550210</number>
<extension>20016</extension>
</phone-number>
</customer>

6.1.1 要素へのマッピング

例6-2のXMLスキーマであると仮定して、図6-1は、2つの複雑なタイプ間での1対1 (1:1)リレーションシップを示します。

例6-2 XMLスキーマのサンプル

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:element name="customer" type="customer-type"/>

<xsd:complexType name="customer-type">
<xsd:element name="phone-number" type="phone-type"/>
</xsd:complexType>

<xsd:complexType name="phone-type">
<xsd:element name="area-code" type="xsd:int"/>
<xsd:element name="number" type="xsd:int"/>
<xsd:element name="extension" type="xsd:int"/>
</xsd:complexType>

</xsd:schema>

図6-1 1対1リレーションシップ

例6-3は、EclipseLinkでこのマッピングを取得するためにJavaクラスに注釈を付ける方法を示しています。JAXBの標準的な@XmlElement注釈を使用して、関連付けられているJavaフィールドをXML要素にマップする必要があることを示すことができます。

注意:
デフォルトでは、JAXBは、Javaオブジェクト上のすべてのフィールドが@XmlElementであるとするため、多くの場合、注釈自体が不要です。ただし、JavaフィールドのXML名をカスタマイズする場合は、name引数を付けて@XmlElement注釈を指定できます。

例6-3 @XmlElement注釈の使用

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlElement(name="phone-number")
private PhoneNumber phoneNumber;

...
}

package example;

import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
public class PhoneNumber {
@XmlElement(name="area-code")
private Integer areaCode;

private Integer number;

private Integer extension;

...
}

例6-4は、EclipseLinkのXMLバインディング・ドキュメントにマッピング情報を定義する方法を示しています。

例6-4 XMLマッピングのサンプル

...
<java-type name="Customer">
<xml-root-element name="customer"/>
<java-attributes>
<xml-element java-attribute="phoneNumber" name="phone-number" type="PhoneNumber"/>
</java-attributes>
</java-type>

<java-type name="PhoneNumber">
<java-attributes>
<xml-value java-attribute="areaCode" name="area-code" type="java.lang.Integer"/>
<xml-value java-attribute="number" type="java.lang.Integer"/>
<xml-value java-attribute="extension" type="java.lang.Integer"/>
</java-attributes>
</java-type>
...

6.1.2 EclipseLinkの@XmlPath注釈の使用

デフォルトでは、Java属性は、その属性のJava名に基づいて、または@XmlElement注釈に指定された名前別に、XMLにマップされます。このマッピングはXPathに基づいており、EclipseLinkの@XmlPath注釈を使用すると、このマッピングをカスタマイズできます。たとえば、これを使用すると、XMLでの要素のネストを制御できます。

例6-5 @XmlPath注釈の使用

package example;

import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlPath("contact-info/phone-number")
private PhoneNumber phoneNumber;

...
}

例6-6 EclipseLinkのXMLバインディングの使用

...
<java-type name="Customer">
<xml-root-element name="customer"/>
<java-attributes>
<xml-element java-attribute="phoneNumber" name="phone-number"
type="PhoneNumber" xml-path="contact-info/phone-number"/>
</java-attributes>
</java-type>
...

これにより、次のXMLが生成されます。

<customer>
<contact-info>
<phone-number>
<number>555-631-2124</number>
</phone-number>
</contact-info>
</customer>

@XmlPathを使用して、XML内での同じ要素の別のオカレンスに索引別にマップすることもできます。次に例を示します。

例6-7 @XmlPath注釈の使用

package example;

import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlPath("contact-info/phone[1]")
private PhoneNumber homePhone;
@XmlPath("contact-info/phone[2]")
private PhoneNumber workPhone;
...
}

生成されるXMLは次のとおりです。

<customer>
<contact-info>
<phone>
<number>555-631-2124</number>
</phone>
<phone>
<number>555-631-8298</number>
</phone>
</contact-info>
</customer>

マッピングでのXPathの使用方法の詳細は、「マッピング用のXPath述語の使用」を参照してください。

6.2 私有の1対多リレーションシップのマッピング

この項では、EclipseLinkとの1対多リレーションシップをマップする方法を示します。

例6-8のスキーマは、図6-2に示されているとおり、CustomerとPhoneNumberの間にある典型的な1対多(1:M)リレーションシップです。

例6-8 XMLマッピングのサンプル

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:element name="customer" type="customer-type"/>

<xsd:complexType name="customer-type">
<xsd:sequence>
<xsd:element name="first-name" type="xsd:string"/>
<xsd:element name="last-name" type="xsd:string"/>
<xsd:element name="phone-number" type="phone-type" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>

<xsd:complexType name="phone-type">
<xsd:sequence>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:element name="number" type="xsd:int"/>
</xsd:sequence>
</xsd:complexType>

</xsd:schema>

図6-2 1対多リレーションシップ

6.2.1 要素へのマッピング

例6-9は、EclipseLinkでこのマッピングを取得するためにJavaクラスに注釈を付ける方法を示しています。JAXBの標準的な@XmlElement注釈をCollectionフィールドまたは配列フィールドで使用すると、これを実現できます。

例6-9 @XmlElement注釈の使用

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlElement(name="first-name")
private String firstName;
@XmlElement(name="last-name")
private String lastName;
@XmlElement(name="phone-number")
private List<PhoneNumber> phoneNumbers;

...
}

package example;

import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;

@XmlAccessorType(XmlAccessType.FIELD)
public class PhoneNumber {
@XmlAttribute
private String type;
private Integer number;

...
}

例6-10は、EclipseLinkのOXMメタデータ形式でマッピング情報を定義する方法を示しています。

例6-10 XMLマッピングのサンプル

...
<java-type name="Customer">
<xml-root-element name="customer"/>
<java-attributes>
<xml-element java-attribute="firstName" name="first-name" type="java.lang.String"/>
<xml-element java-attribute="lastName" name="last-name" type="java.lang.String"/>
<xml-element java-attribute="phoneNumbers" name="phone-number" type="PhoneNumber" container-type="java.util.ArrayList"/>
</java-attributes>
</java-type>

<java-type name="PhoneNumber">
<java-attributes>
<xml-attribute java-attribute="type" type="java.lang.String"/>
<xml-value java-attribute="number" type="java.lang.Integer"/>
</java-attributes>
</java-type>
...

6.2.2 @XmlElementWrapper注釈を使用したグループ化要素

Collectionの要素がグループ化要素の内側に表示されるようにするために、@XmlElementWrapperを使用できます。

例6-11 @XmlElementWrapper注釈の使用

package example;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlElement(name = "phone-number")
@XmlElementWrapper(name="phone-numbers")
private List<PhoneNumber> phoneNumbers;

...
}

これにより、次のXMLが生成されます。

<customer>
<first-name>Bob</first-name>
<last-name>Smith</last-name>
<phone-numbers>
<phone-number type="Home">
<number>5559827222</number>
</phone-number>
<phone-number type="Work">
<number>5558872216</number>
</phone-number>
</phone-numbers>
</customer>

7 共有参照のリレーションシップのマッピング

この章の内容は次のとおりです。

- キーと外部キーの理解

- 単一キーのリレーションシップのマッピング

- コンポジット・キーのリレーションシップのマッピング

- 双方向リレーションシップのマッピング

7.1 キーと外部キーの理解

EclipseLinkでは、次を使用して共有参照キーと外部キーをサポートしています。

- 単一キー

- コンポジット・キー

- 組込みキー・クラス

7.2 単一キーのリレーションシップのマッピング

私有ではないリレーションシップをモデル化するには、ターゲット・オブジェクトにID(キー)が定義されており、かつソース・オブジェクトがこれらのIDを使用してリレーションシップをマップする必要があります。

キーとともに示されるリレーションシップは、@XmlIDおよび@XmlIDREF注釈を使用します。JAXB仕様では@XmlIDのマークの付いているプロパティは文字列である必要がありますが、MOXy JAXBではこの制約は強制されません。

例7-1では、各Employeeは単一のmanagerと複数のreportsを持ちます。

例7-1 @XmlIDおよび@XmlIDREF注釈の使用

package example;

import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
public class Employee {
@XmlAttribute
@XmlID
private Integer id;

@XmlAttribute
private String name;

@XmlIDREF
private Employee manager;

@XmlElement(name="report")
@XmlIDREF
private List<Employee> reports;

...
}

次の例では、EclipseLinkのOXMメタデータ形式でこのマッピング情報を定義する方法を示します。

例7-2 XMLマッピングのサンプル

...
<java-type name="Employee">
<java-attributes>
<xml-attribute java-attribute="id" type="java.lang.Integer" xml-id="true"/>
<xml-attribute java-attribute="name" type="java.lang.String"/>
<xml-element java-attribute="manager" type="mypackage.Employee" xml-idref="true"/>
<xml-element java-attribute="reports" type="mypackage.Employee" container-type="java.util.ArrayList" xml-idref="true"/>
</java-attributes>
</java-type>
...

これにより、次のXMLが生成されます。

<company>
<employee id="1" name="Jane Doe">
<report>2</report>
<report>3</report>
</employee>
<employee id="2" name="John Smith">
<manager>1</manager>
</employee>
<employee id="3" name="Anne Jones">
<manager>1</manager>
</employee>
</company>

managerおよびreports要素には、参照先のEmployeeインスタンスのIDが含まれます。

7.2.1 @XmlListの使用

@XmlIDREF注釈もまた@XmlList注釈と互換性があり、Employeeオブジェクトは次のようにモデル化できます。

例7-3 @XmlList注釈の使用

package example;

import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
public class Employee {
@XmlID
@XmlAttribute
private Integer id;

@XmlAttribute
private String name;

@XmlIDREF
private Employee manager;

@XmlIDREF
@XmlList
private List<Employee> reports;

...
}

これにより、次のXMLが生成されます。

<company>
<employee id="1" name="Jane Doe">
<reports>2 3</reports>
</employee>
<employee id="2" name="John Smith">
<manager>1</manager>
</employee>
<employee id="3" name="Anne Jones">
<manager>1</manager>
</employee>
</company>

7.3 組込みキー・クラスの使用

JPAエンティティに埋込みIDクラスがある場合に、JAXBを使用して一連のJPAエンティティからXML表現を導出できます。

例7-4では、EmployeeIdはEmployeeクラスの組込みIDです。

例7-4 組込みIDのサンプル

@Entity
public class PhoneNumber {

@ManyToOne
@JoinColumns({
@JoinColumn(name="E_ID", referencedColumnName = "E_ID"),
@JoinColumn(name="E_COUNTRY", referencedColumnName = "COUNTRY")
})
private Employee contact;

}

@Entity
@IdClass(EmployeeId.class)
public class Employee {

@EmbeddedId
private EmployeeId id;

@OneToMany(mappedBy="contact")
private List<PhoneNumber> contactNumber;

}

@Embeddable
public class EmployeeId {

@Column(name="E_ID")
private BigDecimal eId;

private String country;

}

JAXBバインディングでは、XMLアクセッサ・タイプはすべてのモデル・クラスのFIELDに設定されます。これは、次に示すようにパッケージ・レベルのJAXB注釈として設定できます。

@XmlAccessorType(XmlAccessType.FIELD)
package com.example.model;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;

例7-5では、JAXB仕様を拡張するEclipseLink拡張機能の@XmlCustomizerを使用します。contact属性は双方向リレーションシップのため、EclipseLink拡張機能の@XmlInverseReferenceを含みます。

例7-5 @XmlCustomizer注釈の使用

@Entity
@IdClass(EmployeeId.class)
@XmlCustomizer(EmployeeCustomizer.class)
public class Employee {

@EmbeddedId
private EmployeeId id;

@OneToMany(mappedBy="contact")
@XmlInverseReference(mappedBy="contact")
private List<PhoneNumber> contactNumber;

}

EmployeeIdクラスのコンテンツをEmployeeクラスに関連付けられた複合型に組み込むには、idプロパティのマッピングのXPathを自己( . )に変更します。その後、IDを表すXMLノードをXPathに指定します。

例7-6 XPathの変更

import org.eclipse.persistence.config.DescriptorCustomizer;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.oxm.mappings.XMLCompositeObjectMapping;

public class EmployeeCustomizer implements DescriptorCustomizer {

public void customize(ClassDescriptor descriptor) throws Exception {
XMLCompositeObjectMapping idMapping =
(XMLCompositeObjectMapping) descriptor.getMappingForAttributeName("id");
idMapping.setXPath(".");

descriptor.addPrimaryKeyFieldName("eId/text()");
descriptor.addPrimaryKeyFieldName("country/text()");
}

}

ターゲット・オブジェクトが単一のIDを持つ場合は、@XmlIDREFを使用します。ターゲット・オブジェクトに複合キーがあるので、フィールド@XmlTransientにマークを付け、EclipseLink拡張機能の@XmlCustomizerを使用してマッピングを設定します。

例7-7 @XmlTransient注釈の使用

@Entity
@XmlCustomizer(PhoneNumberCustomizer.class)
public class PhoneNumber {

@ManyToOne
@JoinColumns({
@JoinColumn(name="E_ID", referencedColumnName = "E_ID"),
@JoinColumn(name="E_COUNTRY", referencedColumnName = "COUNTRY")
})
@XmlTransient
private Employee contact;

}

XMLObjectReferenceMappingが作成されます。マッピングには複数のキー・マッピングが含まれます。

import org.eclipse.persistence.config.DescriptorCustomizer;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.oxm.mappings.XMLObjectReferenceMapping;

public class PhoneNumberCustomizer implements DescriptorCustomizer {

public void customize(ClassDescriptor descriptor) throws Exception {
XMLObjectReferenceMapping contactMapping = new XMLObjectReferenceMapping();
contactMapping.setAttributeName("contact");
contactMapping.setReferenceClass(Employee.class);
contactMapping.addSourceToTargetKeyFieldAssociation("contact/@eID", "eId/text()");
contactMapping.addSourceToTargetKeyFieldAssociation("contact/@country", "country/text()");
descriptor.addMapping(contactMapping);
}

}

7.4 コンポジット・キーのリレーションシップのマッピング

マップするオブジェクトにマルチパート・キー(つまり、一意性を判別するフィールドの組合せ)が含まれている場合、EclipseLinkの@XmlKeyおよび@XmlJoinNodesを使用してこのリレーションシップを設定できます。

特定のクラス内で主キーを宣言する場合、1つ以上の@XmlKey注釈を使用できます。単一キーの場合は、@XmlIDまたは@XmlKeyのいずれかを使用できます。コンポジット主キーの場合、複数の@XmlKey注釈を使用するか、単一の@XmlIDを1つ以上の@XmlKey注釈と組み合せることができます。

注意:
コンポジット・キーは、JAXBを使用してJPAエンティティをマップする場合に便利です。詳細は、『Converting JPA entities to/from XML (via JAXB)』を参照してください。

例7-8では、各Employeeは単一のmanagerと複数のreportsを持ちます。また、Employeesはidフィールドおよびnameフィールドの組合せによって一意に識別されます。

例7-8 @XmlKeyおよび@XmlJoinNodes注釈の使用

package example;

import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;

@XmlAccessorType(XmlAccessType.FIELD)
public class Employee {
@XmlID
@XmlAttribute
private Integer id;

@XmlKey
@XmlAttribute
private String name;

@XmlJoinNodes( {
@XmlJoinNode(xmlPath = "manager/@id", referencedXmlPath = "@id"),
@XmlJoinNode(xmlPath = "manager/@name", referencedXmlPath = "@name") })
public Employee manager;

@XmlJoinNodes( {
@XmlJoinNode(xmlPath = "report/@id", referencedXmlPath = "@id"),
@XmlJoinNode(xmlPath = "report/@name", referencedXmlPath = "@name") })
public List<Employee> reports = new ArrayList<Employee>();

...
}

例7-9では、EclipseLinkのOXMメタデータ形式でこのマッピング情報を定義する方法を示します。

例7-9 XMLマッピングのサンプル

...
<java-type name="Employee">
<java-attributes>
<xml-attribute java-attribute="id" xml-id="true" />
<xml-attribute java-attribute="name" xml-key="true" />
<xml-join-nodes java-attribute="manager">
<xml-join-node xml-path="manager/@id" referenced-xml-path="@id" />
<xml-join-node xml-path="manager/@name" referenced-xml-path="@name" />
</xml-join-nodes>
<xml-join-nodes java-attribute="reports" container-type="java.util.ArrayList">
<xml-join-node xml-path="report/@id" referenced-xml-path="@id" />
<xml-join-node xml-path="report/@name" referenced-xml-path="@name" />
</xml-join-nodes>
</java-attributes>
</java-type>
...

これにより、次のXMLが生成されます。

<company>
<employee id="1" name="Jane Doe">
<report id="2" name="John Smith"/>
<report id="3" name="Anne Jones"/>
</employee>
<employee id="2" name="John Smith">
<manager id="1" name="Jane Doe"/>
</employee>
<employee id="3" name="Anne Jones">
<manager id="1" name="Jane Doe"/>
</employee>
</company>

7.5 双方向リレーションシップのマッピング

EclipseLink MOXyで双方向リレーションシップをマップするには、バックポインタに@XmlInverseReferenceとして注釈が付けられる必要があります。この注釈がないと、マーシャリング中に循環リレーションシップの無限ループが発生します。

@XmlInverseReferencesには、リレーションシップの反対側のプロパティを示すmappedBy属性を指定する必要があります。

例7-11ではEmployeeはPhoneNumbersのコレクションを持ち、各PhoneNumberはEmployeeへのバックポインタを持ちます。

例7-10 @XMlInverseReference注釈の使用

@XmlAccessorType(XmlAccessType.FIELD)
public class Employee {
private String name;
private List<PhoneNumber> phones = new ArrayList<PhoneNumber>();
...
}

@XmlAccessorType(XmlAccessType.FIELD)
public class PhoneNumber {
private String number;
@XmlInverseReference(mappedBy="phones")
private Employee employee;
...
}

例7-11では、EclipseLinkのOXMメタデータ形式でこのマッピングを定義する方法を示します。

例7-11 XMLマッピングのサンプル

...
<java-type name="Employee">
<java-attributes>
<xml-element java-attribute="name" type="java.lang.String"/>
<xml-element java-attribute="phones" type="PhoneNumber" container-type="java.util.ArrayList"/>
</java-attributes>
</java-type>

<java-type name="PhoneNumber">
<java-attributes>
<xml-element java-attribute="number" type="java.lang.String"/>
<xml-inverse-reference java-attribute="employee" type="Employee" mapped-by="phones" />
</java-attributes>
</java-type>
...

In addition, when using @XmlInverseReference, it is not necessary to explicitly set the back-pointer in your Java code; EclipseLink will do this for you automatically:

Employee emp = new Employee();
emp.setName("Bob Smith");

PhoneNumber p = new PhoneNumber();
p.setNumber("555-1212");

emp.getPhones().add(p);

// Not Necessary
// p.setEmployee(emp);

@XmlInverseReferenceバックポインタは、次のタイプのマッピングとともに使用できます。

- 1対1リレーションシップ(「私有の1対1リレーションシップのマッピング」を参照)

- 1対多リレーションシップ(「私有の1対多リレーションシップのマッピング」を参照)

- 単一キーのリレーションシップ(「単一キーのリレーションシップのマッピング」を参照)

- コンポジット・キーのリレーションシップ(「コンポジット・キーのリレーションシップのマッピング」を参照)

@XmlInverseReferenceは、特にJPAエンティティをXMLにマッピングする場合に便利です(「XMLバインディングの使用」を参照)。

8 拡張概念

この章の内容は次のとおりです。

- メタデータのリフレッシュ

- XML名変換のカスタマイズ

- 仮想アクセス・メソッドの使用

- 拡張可能MOXyの使用

- XPath述語を使用したマッピング

- XmlAdapterの使用

- XML変換の使用

- XMLスキーマからのJavaクラスの生成

- 生成されたマッピングのカスタマイズ

8.1 メタデータのリフレッシュ

EclipseLink MOXy 2.3で導入された機能として、実行時にJAXBContextメタデータをリフレッシュできます。これにより、ライブ・アプリケーション環境における既存のマッピングに変更を加え、それらの変更を新しいJAXBContextを作成せずにただちに表示できます。

メタデータ・リフレッシュ機能を使用するには、メタデータ情報が次のいずれかの形式で提供されている必要があります。

- javax.xml.transform.Source

- org.w3c.dom.Node

- org.eclipse.persistence.jaxb.metadata.MetadataSource

例8-1 メタデータのリフレッシュ

この例では、次のEclipseLink OXMファイルからブートストラップされます。

<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="example">
<java-types>
<java-type name="Root">
<java-attributes>
<xml-element java-attribute="name" name="orig-name"/>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>

JAXBContextは次に示す標準の方法で作成されます。

...
ClassLoader classLoader = ClassLoader.getSystemClassLoader();

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
InputStream metadataStream = classLoader.getResourceAsStream("example/eclipselink-oxm.xml";
Document metadataDocument = db.parse(metadataStream);
metadataStream.close();

Map<String, Object> props = new HashMap<String, Object>(1);
props.put(JAXBContextProperties.OXM_METADATA_SOURCE, metadataDocument);
JAXBContext context = JAXBContextFactory.createContext(new Class[] { Root.class }, props);
...

この時点でRootオブジェクトをXMLにマーシャリングする場合、次のようになります。

<root>
<orig-name>RootName</orig-name>
</root>

この例では、メタデータ・ドキュメントを直接修正して名前フィールドのXML名を変更します。その後、次のrefreshMetadata() APIを使用してメタデータをリフレッシュできます。

...
Element xmlElementElement = (Element) metadataDocument.getElementsByTagNameNS("http://www.eclipse.org/eclipselink/xsds/persistence/oxm", "xml-element").item(0);
xmlElementElement.setAttribute("name", "new-name");
JAXBHelper.getJAXBContext(jc).refreshMetadata();
...

メタデータのリフレッシュ後、同一のRootオブジェクトが次のようにマーシャリングされます。

<root>
<new-name>RootName</new-name>
</root>

8.2 XML名変換のカスタマイズ

JAXBは、Java名からXML名への変換のための確立したルールです。これは、注釈の使用によりオーバーライドできます。一般的なネーミング・ルール(すべてを大文字にするなど)に従うことが難しい場合もあります。EclipseLink MOXy 2.3以降では、デフォルトのネーミング・アルゴリズムをオーバーライドできます。

この例では、XMLNameTransformerの実装を作成してMOXyにネーミング・アルゴリズムを提供します。

8.2.1 XMLNameTransformerの使用

XMLNameTransformerインタフェースでは、次のような名前生成のカスタマイズのための様々なメソッドを定義します。

- transformElementName: Javaフィールドまたはメソッドから要素を作成するときにコールされます。

- transformAttributeName: Javaフィールドまたはメソッドから属性を作成するときにコールされます。

- transformTypeName: Javaクラスから単純型または複合型を作成するときにコールされます。

- transformRootElementName: Javaクラスから(ルート)単純型または複合型を作成するときにコールされます。

例8-2では、次のことを行うXMLNameTransformerを定義します。

- ルート要素は非修飾Javaクラス名になります。

- その他の型は(非修飾Javaクラス名) + "Type"という名前になります。

- キャメル・ケース要素名は、小文字のハイフンで連結された名前に変換されます。

- XML属性はすべて大文字で表示されます。

例8-2 XMLNameTransformerの使用

package example;

public class NameGenerator implements org.eclipse.persistence.oxm.XMLNameTransformer {

// Use the unqualified class name as our root element name.
public String transformRootElementName(String name) {
return name.substring(name.lastIndexOf('.') + 1);
}

// The same algorithm as root element name plus "Type" appended to the end.
public String transformTypeName(String name) {
return transformRootElementName(name) + "Type";
}

// The name will be lower-case with word breaks represented by '-'.
// Note:  A capital letter in the original name represents the start of a new word.
public String transformElementName(String name) {
StringBuilder strBldr = new StringBuilder();
for (char character : name.toCharArray()) {
if (Character.isUpperCase(character)) {
strBldr.append('-');
strBldr.append(Character.toLowerCase(character));
} else {
strBldr.append(character);
}
}
return strBldr.toString();
}

// The original name converted to upper-case.
public String transformAttributeName(String name) {
return name.toUpperCase();
}

}

8.2.2 サンプル・モデル

例8-3のドメイン・モデルが使用されます。スペースの都合上、アクセッサは省略します。

例8-3 Customer

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlType(propOrder={"fullName", "shippingAddress"})
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {

@XmlAttribute
private long id;

private String fullName;

private Address shippingAddress;

}

例8-4 Address.java

import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
public class Address {

@XmlAttribute
private String type;

private String street;

}

8.2.3 ネーミング・アルゴリズムの指定

ネーミング・アルゴリズムの実装は、@XmlNameTransformer注釈(パッケージ・レベルまたはタイプ・レベル)を介して、またはXMLの外部バインディング・ファイルを介して提供されます。

1. タイプ・レベル:

@XmlNameTransformer(example.NameGenerator.class)
public class Customer

2. パッケージ・レベル(package-info.java):

@XmlNameTransformer(example.NameGenerator.class)
package example;

3. 外部バインディング・ファイル:

<?xml version='1.0' encoding='UTF-8'?>
<xml-bindings xmlns='http://www.eclipse.org/eclipselink/xsds/persistence/oxm' xml-name-transformer='example.NameGenerator'>
<xml-schema/>
<java-types/>
</xml-bindings>

8.2.4 XML出力

カスタマイズされていない場合、JAXBのデフォルトのネーミング・アルゴリズムでは、次のようなXMLが生成されます。

<?xml version="1.0" encoding="UTF-8"?>
<customer id="123">
<fullName>Jane Doe</fullName>
<shippingAddress type="residential">
<street>1 Any Street</street>
</shippingAddress>
</customer>

カスタマイズ済のネーミング・アルゴリズムを利用すると、ドメイン・クラスに追加のメタデータを指定せずに、次の出力を取得できます。

<?xml version="1.0" encoding="UTF-8"?>
<Customer ID="123">
<full-name>Jane Doe</full-name>
<shipping-address TYPE="residential">
<street>1 Any Street</street>
</shipping-address>
</Customer>

8.3 仮想アクセス・メソッドの使用

(Javaフィールドおよびアクセッサ・メソッドで表される)標準JAXBプロパティに加え、EclipseLink MOXy 2.3では、特別なget()およびset()メソッドに依存してマッピング・データを維持する仮想プロパティおよび仮想アクセス・メソッドの概念が導入されました。たとえば、特定のマッピングのためのデータ保持の基礎となる構造として、HashMapを使用する場合があります。仮想メソッド・アクセスを使用するマッピングが、EclipseLink OXMメタデータに定義されている必要があります。

エンティティに仮想プロパティを追加するには、次の条件を満たす必要があります。

- Javaクラスに@XmlVirtualAccessMethods注釈またはOXMの<xml-virtual-access-methods>要素のマークが付いている必要があります。

- Javaクラスに次の仮想プロパティ値にアクセスするためのgetterメソッドおよびsetterメソッドが含まれている必要があります。

- public <ValueType> get(String propertyName)

- public void set(String propertyName, <ValueType> value)

- メソッド名は構成可能です。

- <ValueType>はObjectまたはその他のJavaタイプ(メソッド・シグネチャで特定の値タイプ・クラスを使用する場合)にできます。

注意:
デフォルトでは、EclipseLinkはsetおよびgetという名前のメソッドを探します。アクセッサ・メソッドの名前をカスタマイズする方法は、「代替のアクセッサ・メソッドの指定」を参照してください。

マルチテナント・アーキテクチャで仮想プロパティを使用する例は、「拡張可能MOXyの使用」を参照してください。

8.3.1 仮想アクセス・メソッドの構成

仮想アクセス・メソッドは、Java注釈(例8-5を参照)またはEclipseLink OXMメタデータ(例8-6を参照)を通して構成できます。

例8-5 Java注釈の使用

package example;

import java.util.Map;
import java.util.HashMap;

import javax.xml.bind.annotation.*;

import org.eclipse.persistence.oxm.annotations.XmlVirtualAccessMethods;

@XmlRootElement
@XmlVirtualAccessMethods
@XmlAccessorType(XmlAccessType.PROPERTY)
public class Customer {

private int id;

private String name;

private Map<String, Object> extensions = new HashMap<String, Object>();

public Object get(String name) {
return extensions.get(name);
}

public void set(String name, Object value) {
extensions.put(name, value);
}

@XmlAttribute
public int getId() {
...

}

例8-6 OXMメタデータの使用

...
<java-types>
<java-type name="Customer">
<xml-virtual-access-methods />
<java-attributes>
<xml-attribute java-attribute="id" />
<xml-element java-attribute="name" />
</java-attributes>
</java-type>
...

8.3.2 例

この例では、Customerクラス(例8-3)を仮想マッピングを定義するEclipseLink OXMファイルとともに使用します。このファイルに出現する、対応するJava属性を持たないプロパティは、仮想プロパティとみなされ、仮想アクセス・メソッドを介してアクセスされます。関連付けられたJavaフィールドがないため、type情報も指定する必要があります。

例8-7 virtualprops-oxm.xmlファイル

...
<java-types>
<java-type name="Customer">
<java-attributes>
<xml-element java-attribute="discountCode" name="discount-code"
type="java.lang.String" />
</java-attributes>
</java-type>
</java-types>
...

JAXBContextの作成時に、virtualpropsメタデータをCustomerクラスとともに渡します。

仮想プロパティの値を設定するには、前述のset()メソッドを使用します。

InputStream oxm = classLoader.getResourceAsStream("virtualprops-oxm.xml");
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, oxm);

Class[] classes = new Class[] { Customer.class };
JAXBContext ctx = JAXBContext.newInstance(classes, properties);

Customer c = new Customer();
c.setId(7761);
c.setName("Bob Smith");
c.set("discountCode", "SIUB372JS7G2IUDS7");

ctx.createMarshaller().marshal(e, System.out);

これにより、次のXMLが生成されます。

<customer id="7761">
<name>Bob Smith</name>
<discount-code>SIUB372JS7G2IUDS7</discount-code>
</customer>

反対に、仮想プロパティにアクセスするにはget(String)メソッドを使用します。

...
Customer c = (Customer) ctx.createUnmarshaller().unmarshal(CUSTOMER_URL);

// Populate UI
customerWindow.getTextField(ID).setText(String.valueOf(c.getId()));
customerWindow.getTextField(NAME).setText(c.getName());
customerWindow.getTextField(DCODE).setText(c.get("discountCode"));
...

8.3.3 XmlAccessType.FIELDおよびXmlTransientの使用

XmlAccessType.FIELDの@XmlAccessorTypeを使用している場合、Map自身がXMLにバインドされるのを避けるため、仮想プロパティのMap属性を@XmlTransientとマークする必要があります。

例8-8 Map属性のマーク付け

package example;

import javax.xml.bind.annotation.*;

import org.eclipse.persistence.oxm.annotations.XmlVirtualAccessMethods;

@XmlRootElement
@XmlVirtualAccessMethods
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {

@XmlTransient
private Map<String, Object> extensions;
...

8.3.4 オプション

- 代替のアクセッサ・メソッドの指定

- スキーマ生成オプションの指定

8.3.4.1 代替のアクセッサ・メソッドの指定

仮想メソッド・アクセッサとして異なるメソッド名を使用するには、@XmlVirtualAccessMethodsでgetMethodName属性およびsetMethodName属性を使用してその名前を指定します。

例8-9 代替のアクセッサ・メソッドの使用

package example;

import java.util.Properties;

import javax.xml.bind.annotation.*;

import org.eclipse.persistence.oxm.annotations.XmlVirtualAccessMethods;

@XmlRootElement
@XmlVirtualAccessMethods(getMethod = "getCustomProps", setMethod = "putCustomProps")
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {

@XmlAttribute
private int id;

private String name;

@XmlTransient
private Properties<String, Object> props = new Properties<String, Object>();

public Object getCustomProps(String name) {
return props.getProperty(name);
}

public void putCustomProps(String name, Object value) {
props.setProperty(name, value);
}

}

OXMで、次のようにします。

例8-10 xml-virtual-access-methods要素の使用

...
<java-types>
<java-type name="Customer">
<xml-virtual-access-methods get-method="getCustomProps" set-method="putCustomProps" />
<java-attributes>
<xml-attribute java-attribute="id" />
<xml-element java-attribute="name" />
<!-- virtual -->
<xml-element java-attribute="discountCode" name="discount-code"
type="java.lang.String" />
</java-attributes>
</java-type>
...

8.3.4.2 スキーマ生成オプションの指定

@XmlVirtualAccessMethodsでschema属性を使用して、生成されたスキーマでの仮想プロパティの表示方法を構成できます。EclipseLinkには、2つのオプションがあります。仮想プロパティは次のことができます。

- 個々のノードとして記述します。

- 単一の<any>要素に統合します。

8.3.4.2.1 個々のノードとしての仮想プロパティ

これは、EclipseLinkのデフォルトの動作ですが、次のようにオーバーライドを明示的に指定できます。

例8-11 個々のノードとしてのマッピング

package example;

@XmlRootElement
@XmlVirtualAccessMethods(schema = XmlVirtualAccessMethodsSchema.NODES)
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {

...

次に例を示します。

例8-12 元のCustomerスキーマ

<xs:schema ...>

<xs:element name="customer">
<xs:complexType>
<xs:sequence>
<xs:element name="first-name" type="xs:string" />
<xs:element name="last-name" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>

</xs:schema>

例8-13 生成されたスキーマ(middle-initialとphone-numberの追加後)

<xs:schema ...>

<xs:element name="customer">
<xs:complexType>
<xs:sequence>
<xs:element name="first-name" type="xs:string" />
<xs:element name="last-name" type="xs:string" />
<xs:element name="middle-initial" type="xs:string" />
<xs:element name="phone-number" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>

</xs:schema>

8.3.4.2.2 <any>要素の仮想プロパティ

EclipseLinkは、<any>要素を使用して1つのノードにすべての仮想プロパティを格納することもできます。

例8-14 <any>要素の使用

package example;

@XmlRootElement
@XmlVirtualAccessMethods(schema = XmlVirtualAccessMethodsSchema.ANY)
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {

...

例8-14で、このアプローチを使用して新しく生成されたスキーマは、次のようになります。

例8-15 生成されたスキーマ

<xs:schema ...>

<xs:element name="customer">
<xs:complexType>
<xs:sequence>
<xs:element name="first-name" type="xs:string" />
<xs:element name="last-name" type="xs:string" />
<xs:any minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>

</xs:schema>

8.4 拡張可能MOXyの使用

マルチテナント・アーキテクチャでは、サーバーで1つのアプリケーションが動作して、複数のクライアント組織(テナント)にサービスを提供します。優れたマルチテナント・アプリケーションでは、テナントごとのカスタマイズが可能です。これらのカスタマイズがデータに対して行われる場合は、バインディング・レイヤーでカスタマイズを処理するのが難しい場合があります。

JAXBは、実際のフィールドおよびプロパティがあるドメイン・モデルを操作するように設計されています。EclipseLink MOXy仮想プロパティは、ソースを変更しなくてもクラスを拡張できます。

8.4.1 @XmlVirtualAccessMethods注釈の使用

クラスが拡張可能であることを指定するには、@XmlVirtualAccessMethods注釈が使用されます。プロパティ名によって値を返すgetメソッド、およびプロパティ名によって値を格納するsetメソッドには、拡張可能クラスが必要です。これらのメソッドのデフォルト名は、getおよびsetで、@XmlVirtualAccessMethods注釈でオーバーライドできます。

この例では複数の拡張可能クラスがあるため、拡張可能クラスが拡張できる、この動作のベース・クラスを構成します。@XmlTransient注釈を使用することにより、ExtensibleBaseが継承関係としてマップされないようにします。モデル内で、すべてのテナントに共通な部分は、実際のプロパティで表されます。テナントごとの拡張機能は、仮想プロパティとして表されます。

例8-16 ExtensibleBaseのサンプル

package examples.virtual;

import java.util.HashMap;
import java.util.Map;

import javax.xml.bind.annotation.XmlTransient;

import org.eclipse.persistence.oxm.annotations.XmlVirtualAccessMethods;

@XmlTransient
@XmlVirtualAccessMethods(setMethod="put")
public class ExtensibleBase {

private Map<String, Object> extensions = new HashMap<String, Object>();

public <T> T get(String property) {
return (T) extensions.get(property);
}

public void put(String property, Object value) {
extensions.put(property, value);
}
}

例8-17 Customer

Customerクラスは、@XmlVirtualAccessMethods注釈が付けられたドメイン・クラスから継承するので拡張可能になります。

package examples.virtual;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Customer extends ExtensibleBase {

private String firstName;
private String lastName;
private Address billingAddress;

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 Address getBillingAddress() {
return billingAddress;
}

public void setBillingAddress(Address billingAddress) {
this.billingAddress = billingAddress;
}

}

例8-18 Address

モデル内のすべてのクラスを拡張可能にする必要はありません。この例のAddressクラスには、仮想プロパティはありません。

package examples.virtual;

public class Address {

private String street;

public String getStreet() {
return street;
}

public void setStreet(String street) {
this.street = street;
}

}

例8-19 PhoneNumber

Customerと同様に、PhoneNumberは拡張可能なクラスになります。

package examples.virtual;

import javax.xml.bind.annotation.XmlValue;

public class PhoneNumber extends ExtensibleBase {

private String number;

@XmlValue
public String getNumber() {
return number;
}

public void setNumber(String number) {
this.number = number;
}

}

8.4.2 テナントの作成1

最初のテナントは、オンライン・スポーツ用品店で、次のようにモデルを拡張する必要があります。

- 顧客ID

- 顧客のミドル・ネーム

- 配送先住所

- 連絡先電話番号のコレクション

- 電話番号のタイプ(自宅、勤務先または携帯など)

仮想プロパティのメタデータは、MOXyのXMLマッピング・ファイルを介して指定します。仮想プロパティは、実際のプロパティと同様の方法でマップされます。タイプ(リフレクションでは特定できないため)や、コレクション・プロパティの場合はコンテナ・タイプなど、追加情報が必要です。

例8-20のCustomerで定義されている仮想プロパティは、middleName、shippingAddressおよびphoneNumbersです。PhoneNumberの仮想プロパティはtypeプロパティです。

例8-20 binding-tenant1.xml

<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="examples.virtual">
<java-types>
<java-type name="Customer">
<xml-type prop-order="firstName middleName lastName billingAddress shippingAddress phoneNumbers"/>
<java-attributes>
<xml-attribute
java-attribute="id"
type="java.lang.Integer"/>
<xml-element
java-attribute="middleName"
type="java.lang.String"/>
<xml-element
java-attribute="shippingAddress"
type="examples.virtual.Address"/>
<xml-element
java-attribute="phoneNumbers"
name="phoneNumber"
type="examples.virtual.PhoneNumber"
container-type="java.util.List"/>
</java-attributes>
</java-type>
<java-type name="PhoneNumber">
<java-attributes>
<xml-attribute
java-attribute="type"
type="java.lang.String"/>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>

get/setメソッドは、実際のプロパティを操作するためにドメイン・モデルで使用し、@XmlVirtualAccessMethods注釈に定義されているアクセッサは、仮想プロパティを操作するために使用します。通常のJAXBメカニズムは、次に示す操作のマーシャリングおよびアンマーシャリングに使用します。

Customer customer = new Customer();

//Set Customer's real properties
customer.setFirstName("Jane");
customer.setLastName("Doe");

Address billingAddress = new Address();
billingAddress.setStreet("1 Billing Street");
customer.setBillingAddress(billingAddress);

//Set Customer's virtual 'middleName' property
customer.put("middleName", "Anne");

//Set Customer's virtual 'shippingAddress' property
Address shippingAddress = new Address();
shippingAddress.setStreet("2 Shipping Road");
customer.put("shippingAddress", shippingAddress);

List<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>();
customer.put("phoneNumbers", phoneNumbers);

PhoneNumber workPhoneNumber = new PhoneNumber();
workPhoneNumber.setNumber("555-WORK");
//Set the PhoneNumber's virtual 'type' property
workPhoneNumber.put("type", "WORK");
phoneNumbers.add(workPhoneNumber);

PhoneNumber homePhoneNumber = new PhoneNumber();
homePhoneNumber.setNumber("555-HOME");
//Set the PhoneNumber's virtual 'type' property
homePhoneNumber.put("type", "HOME");
phoneNumbers.add(homePhoneNumber);

Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, "examples/virtual/binding-tenant1.xml");
JAXBContext jc = JAXBContext.newInstance(new Class[] {Customer.class, Address.class}, properties);

Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(customer, System.out);

例8-21 出力

<?xml version="1.0" encoding="UTF-8"?>
<customer>
<firstName>Jane</firstName>
<middleName>Anne</middleName>
<lastName>Doe</lastName>
<billingAddress>
<street>1 Billing Street</street>
</billingAddress>
<shippingAddress>
<street>2 Shipping Road</street>
</shippingAddress>
<phoneNumber type="WORK">555-WORK</phoneNumber>
<phoneNumber type="HOME">555-HOME</phoneNumber>
</customer>

8.4.3 テナントの作成2

2番目のテナントは、オンデマンドの映画や音楽を提供するストリーミング・メディア・プロバイダです。ここでは、コア・モデルに対して単一の連絡先電話番号を拡張する必要があります。

このテナントの場合、例8-22に示すように、マッピング・ファイルも利用して実際のプロパティのマッピングをカスタマイズします。

例8-22 binding-tenant2.xml

<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="examples.virtual">
<xml-schema namespace="urn:tenant1" element-form-default="QUALIFIED"/>
<java-types>
<java-type name="Customer">
<xml-type prop-order="firstName lastName billingAddress phoneNumber"/>
<java-attributes>
<xml-attribute java-attribute="firstName"/>
<xml-attribute java-attribute="lastName"/>
<xml-element java-attribute="billingAddress" name="address"/>
<xml-element
java-attribute="phoneNumber"
type="examples.virtual.PhoneNumber"/>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>

Customer customer = new Customer();
customer.setFirstName("Jane");
customer.setLastName("Doe");

Address billingAddress = new Address();
billingAddress.setStreet("1 Billing Street");
customer.setBillingAddress(billingAddress);

PhoneNumber phoneNumber = new PhoneNumber();
phoneNumber.setNumber("555-WORK");
customer.put("phoneNumber", phoneNumber);

Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, "examples/virtual/binding-tenant2.xml");
JAXBContext jc = JAXBContext.newInstance(new Class[] {Customer.class, Address.class}, properties);

Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(customer, System.out);

例8-23 出力

両方のテナントはいくつかの実際のプロパティを共有していますが、次のように仮想プロパティが指定されているので、対応するXML表現はまったく異なるものにできます。

<?xml version="1.0" encoding="UTF-8"?>
<customer xmlns="urn:tenant1" firstName="Jane" lastName="Doe">
<address>
<street>1 Billing Street</street>
</address>
<phoneNumber>555-WORK</phoneNumber>
</customer>

8.5 XPath述語を使用したマッピング

デフォルトでは、JAXBはJavaのフィールド名をXML要素名として使用します。

例8-24 JavaコードとXMLスキーマのサンプル

public class Customer {
@XmlElement
private String firstName;
@XmlElement
private String lastName;
}

<?xml version="1.0" encoding="UTF-8"?>
<customer>
<firstName>Bob</firstName>
<lastName>Roberts</lastName>
</customer>

また、XML名は次のように@XmlElement注釈の名前属性を使用してカスタマイズできます。

例8-25 JavaコードとXMLスキーマのサンプル

public class Customer {
@XmlElement(name="f-name")
private String firstName;
@XmlElement(name="l-name")
private String lastName;
}

<?xml version="1.0" encoding="UTF-8"?>
<customer>
<f-name>Bob</f-name>
<l-name>Roberts</l-name>
</customer>

ただし、場合によっては、ドキュメント内の位置に基づいて、または要素の属性値に基づいて要素がマップされる必要があります。

<?xml version="1.0" encoding="UTF-8"?>
<node>
<name>Jane</name>
<name>Doe</name>
<node name="address">
<node name="street">123 A Street</node>
</node>
<node name="phone-number" type="work">555-1111</node>
<node name="phone-number" type="cell">555-2222</node>
</node>

このような場合、EclipseLink MOXyでXPath述語を使用して、XML要素の名前を指定する式を定義できます。

8.5.1 XPath述語によるマッピング

XPath述語は、指定した要素に対して評価する式を表します。たとえば、XPath文は次のようになります。

node[2]

これは、2番目に出現するnode要素(DEF)と一致します。

<?xml version="1.0" encoding="UTF-8"?>
<data>
<node>ABC</node>
<node>DEF</node>
</data>

また、述語は次の属性値に基づいて照合することもできます。

node[@name='foo']

これは、属性の名前がfoo(つまりABC)のnode要素と一致します。DEFを含むノードとは一致しません。

<?xml version="1.0" encoding="UTF-8"?>
<data>
<node name="foo">ABC</node>
<node name="bar">DEF</node>
</data>

注意:
XPath述語の詳細は、『XML Path Language (XPath) specification』(http://www.w3.org/TR/xpath)の「2.4 Predicates」を参照してください。

8.5.2 位置に基づくマッピング

次の例では、2つのname要素がXMLに含まれています。最初の名前は顧客の名を表し、2番名の名前は姓を表しています。これをマップするには、適切なXML要素に一致する各プロパティに対してXPath式を指定します。また、@XmlType(propOrder)も使用して、要素が常に正しい位置に配置されるようにします。

例8-26 @XmlType(propOrder)注釈の使用

package example;

import javax.xml.bind.annotation.*;

import org.eclipse.persistence.oxm.annotations.XmlPath;

@XmlRootElement
@XmlType(propOrder={"firstName", "lastName"})
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlPath("name[1]/text()")
private String firstName;

@XmlPath("name[2]/text()")
private String lastName;

...
}

同一の構成は、次に示すEclipseLink XMLバインディングに関するドキュメントでも表現できます。

例8-27 prop-order属性の使用

...
<java-type name="Customer">
<xml-root-element/>
<xml-type prop-order="firstName lastName"/>
<java-attributes>
<xml-element java-attribute="firstName" xml-path="name[1]/text()"/>
<xml-element java-attribute="lastName" xml-path="name[2]/text()"/>
</java-attributes>
</java-type>
...

これにより、目的のXML表現が生成されます。

例8-28 結果となるXML

<?xml version="1.0" encoding="UTF-8"?>
<customer>
<name>Bob</name>
<name>Smith</name>
</customer>

8.5.3 属性値に基づくマッピング

EclipseLink MOXy 2.3以降では、属性値に基づいてXML要素にマップすることもできます。この例では、すべてのXML要素は名前付きノードですが、次の名前属性の値によって区別されています。

例8-29 XMLスキーマのサンプル

<?xml version="1.0" encoding="UTF-8"?>
<node>
<node name="first-name">Bob</node>
<node name="last-name">Smith</node>
<node name="address">
<node name="street">123 A Street</node>
</node>
<node name="phone-number" type="work">555-1111</node>
<node name="phone-number" type="cell">555-2222</node>
</node>

XPathをelement-name[@attribute-name='value']の形式で使用して各Javaフィールドをマップできます。

例8-30 マッピングのサンプル

package example;

import javax.xml.bind.annotation.*;

import org.eclipse.persistence.oxm.annotations.XmlPath;

@XmlRootElement(name="node")
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {

@XmlPath("node[@name='first-name']/text()")
private String firstName;

@XmlPath("node[@name='last-name']/text()")
private String lastName;

@XmlPath("node[@name='address']")
private Address address;

@XmlPath("node[@name='phone-number']")
private List<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>();

...
}

package example;

import javax.xml.bind.annotation.*;

import org.eclipse.persistence.oxm.annotations.XmlPath;

@XmlAccessorType(XmlAccessType.FIELD)
public class Address {

@XmlPath("node[@name='street']/text()")
private String street;

...
}

package example;

import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
public class PhoneNumber {

@XmlAttribute
private String type;

@XmlValue
private String number;

...
}

8.5.4 セルフ・マッピングの作成

EclipseLinkでは、ターゲット・オブジェクトからのデータがソース・オブジェクトのXML要素内に表示されるように1対1のマッピングを構成することもできます。前述の例を拡張して、Address情報をマッピングして、自身の要素内にラップするのではなく、customer要素の下に直接表示させることができます。これは、セルフ・マッピングと呼ばれ、ターゲット・オブジェクトのXPathを . (ドット)に設定することで実現されます。

例8-31に、注釈で宣言するセルフ・マッピングを示します。

例8-31 セルフ・マッピングの例

package example;

import javax.xml.bind.annotation.*;

import org.eclipse.persistence.oxm.annotations.XmlPath;

@XmlRootElement(name="node")
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {

@XmlPath("node[@name='first-name']/text()")
private String firstName;

@XmlPath("node[@name='last-name']/text()")
private String lastName;

@XmlPath(".")
private Address address;

@XmlPath("node[@name='phone-number']")
private List<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>();

...
}

EclipseLinkでセルフ・マッピングを使用すると、目的のXMLが生成されます。streetデータはルート・ノードに格納されます。

<?xml version="1.0" encoding="UTF-8"?>
<node>
<node name="first-name">Bob</node>
<node name="last-name">Smith</node>
<node name="street">123 A Street</node>
<node name="phone-number" type="work">555-1111</node>
<node name="phone-number" type="cell">555-2222</node>
</node>

8.6 XmlAdapterの使用

一部のJavaクラスは、JAXBでの使用に適したものではない場合があり、一見したところマップ可能ではないように見えます。たとえば、デフォルトのno-argコンストラクタを持たないクラスや、XML表現が自動的に判別されないクラスなどです。JAXBのXmlAdapterを使用すると、カスタム・コードを定義してマップ可能ではないクラスをJAXBが操作できるものに変換できます。次に、@XmlJavaTypeAdapter注釈を使用して、マップ可能ではないクラスを操作するときはアダプタを使用するよう示すことができます。

XmlAdapterでは次の用語が使用されます。

- ValueType: JAXBがデフォルトで操作方法を認識している場合のタイプ。

- BoundType: JAXBが操作方法を認識していない場合のタイプ。このタイプがValueTypeを通してメモリー内の表現として使用できるよう、アダプタが記述されます。

XmlAdapterクラスの概要は次のとおりです。

例8-32 XmlAdapterクラスの概要

public class AdapterName extends XmlAdapter<ValueType, BoundType> {

public BoundType unmarshal(ValueType value) throws Exception {
...
}

public ValueType marshal(BoundType value) throws Exception {
...
}

}

8.6.1 java.util.Currencyの使用

1番目の例では、次のドメイン・クラスを使用します。

例8-33 ドメイン・クラスのサンプル

package example;

import java.util.Currency;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class PurchaseOrder {

private Double amount;

private Currency currency;

...
}

ここでは、CurrencyはJAXBでは自動的にマップされません。引数なしのコンストラクタが含まれていないためです。ただし、CurrencyをJAXBで操作できる単純なStringに変換するアダプタを記述できます。ここでは、幸いにもCurrencyのtoString()メソッドは、新しいCurrencyの作成に使用できる通貨コードを戻します。

例8-34 アダプタの使用

package example;

import java.util.Currency;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class CurrencyAdapter extends XmlAdapter<String, Currency> {

/*
* Java => XML
* Given the unmappable Java object, return the desired XML representation.
*/
public String marshal(Currency val) throws Exception {
return val.toString();
}

/*
* XML => Java
* Given an XML string, use it to build an instance of the unmappable class.
*/
public Currency unmarshal(String val) throws Exception {
return Currency.getInstance(val);
}

}

アダプタをCurrencyプロパティのために使用することを示すため、@XmlJavaTypeAdapterで注釈を付け、アダプタのクラス名を指定します。

例8-35 @XmlJavaTypeAdapter注釈の使用

package example;

import java.util.Currency;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class PurchaseOrder {

private Double amount;

@XmlJavaTypeAdapter(CurrencyAdapter.class)
private Currency currency;

...
}

8.6.2 java.awt.Pointの使用

時として、マップ可能ではないクラスを処理する最適な方法は、JAXBでマップできるかわりのクラスを記述し、XmlAdapter内で2つのクラス間で変換を行うことです。この例では、Pointクラスを使用します。このクラスのgetLocation()メソッド(JAXBが自動的に取得してマップする)により、マーシャリング中に無限ループが発生します。Pointクラスは変更できないため、新しいクラスのMyPointを記述してアダプタ内で使用します。

例8-36 java.awt.Pointの使用

package example;

public class MyPoint {

private int x, y;

public MyPoint() {
this(0, 0);
}

public MyPoint(int x, int y) {
this.x = x;
this.y = y;
}

public int getX() {
return x;
}

...
}

package example;

import java.awt.Point;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class MyPointAdapter extends XmlAdapter<MyPoint, Point> {

/*
* Java => XML
*/
public MyPoint marshal(Point val) throws Exception {
return new MyPoint((int) val.getX(), (int) val.getY());
}

/*
* XML => Java
*/
public Point unmarshal(MyPoint val) throws Exception {
return new Point(val.getX(), val.getY());
}

}

これで、Pointプロパティが@XmlJavaTypeAdapterでマーク付けされました。

例8-37 @XmlJavaTypeAdapter注釈の使用

package example;

import java.awt.Point;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Zone {

private String name;

@XmlJavaTypeAdapter(MyPointAdapter.class)
private Point startCoord;

@XmlJavaTypeAdapter(MyPointAdapter.class)
private Point endCoord;

...
}

8.6.3 パッケージ・レベル・アダプタの指定

例8-37では、両方のPointプロパティに@XmlJavaTypeAdapter注釈で注釈付けをしています。ドメイン・クラスなどにこれらのプロパティのタイプが多数ある場合、パッケージ・レベルで@XmlJavaTypeAdaptersを指定するとより便利です。

両方のアダプタ・クラスをpackage-info.javaに定義できます。その場合、CurrencyプロパティやPointプロパティに注釈付けする必要がなくなります。

@XmlJavaTypeAdapters({
@XmlJavaTypeAdapter(value=CurrencyAdapter.class,type=Currency.class),
@XmlJavaTypeAdapter(value=MyPointAdapter.class,type=Point.class)
})
package example;

8.6.4 クラス・レベルの@XmlJavaTypeAdaptersの指定

Javaクラスがあり、マーシャリングおよびアンマーシャリング中に常にXmlAdapterを使用する場合、次のように@XmlJavaTypeAdapterをクラス・レベルで直接指定します。

package example;

import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlJavaTypeAdapter(DataStructureAdapter.class)
public class DataStructure {

...

}

これで、DataStructureプロパティを持つオブジェクトが、プロパティ自身に注釈付けを行うことなく自動的にDataStructureAdapterを使用するようになります。

8.7 XML変換の使用

多くの場合、MOXyの@XmlTransformation注釈を使用して、オブジェクトのマーシャリングおよびアンマーシャリングをかなり制御できます。1つ以上のXMLノードがJava属性の値の作成に使用できる場合に、@XmlTransformationを使用してカスタム・マッピングを作成できます。

マーシャリング(書込み)およびアンマーシャリング(読取り)時のカスタム要件を処理するには、@XmlTransformationでorg.eclipse.persistence.mappings.transformersのインスタンス(AttributeTransformerやFieldTransformerなど)を使用します。これにより、ドメイン・オブジェクトが特別なインタフェースを実装する必要がない非侵入型ソリューションが提供されます。

たとえば、次のXMLをオブジェクトにマップしてDATEとTIMEの値をsingle java.util.Dateオブジェクトに結合する場合、@XmlTransformation:を使用できます。

<ELEM_B>
<B_DATE>20100825</B_DATE>
<B_TIME>153000</B_TIME>
<NUM>123</NUM>
<C_DATE>20100825</C_DATE>
<C_TIME>154500</C_TIME>
</ELEM_B>

注意:
通常は@XmlAdapterを使用します。ただし、次のことに注意してください。

- DATE/TIMEのペアがドキュメント内で繰り返されますが、要素名は毎回変わります(B_DATE/B_TIMEやC_DATE/C_TIMEなど)。

- 各ペアにはグループ化要素がないため、ElemBクラス全体を適応させる必要があります。

このような問題があるため、MOXyのトランスフォーメーション・マッピングのほうがより実装がより容易です。

例8-38 マッピングの例

package example;

import java.util.Date;

import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name="ELEM_B")
public class ElemB {

@XmlTransformation
@XmlReadTransformer(transformerClass=DateAttributeTransformer.class)
@XmlWriteTransformers({
@XmlWriteTransformer(xmlPath="B_DATE/text()", transformerClass=DateFieldTransformer.class),
@XmlWriteTransformer(xmlPath="B_TIME/text()", transformerClass=TimeFieldTransformer.class),
})
private Date bDate;

@XmlElement(name="NUM")
private int num;

@XmlTransformation
@XmlReadTransformer(transformerClass=DateAttributeTransformer.class)
@XmlWriteTransformers({
@XmlWriteTransformer(xmlPath="C_DATE/text()", transformerClass=DateFieldTransformer.class),
@XmlWriteTransformer(xmlPath="C_TIME/text()", transformerClass=TimeFieldTransformer.class),
})
private Date cDate;

}

8.7.1 AttributeTransformerの使用

AttributeTransformerを使用してJava属性値を構成します。

例8-39 AttributeTransfomerのサンプル

package example;

import java.text.ParseException;
import java.text.SimpleDateFormat;

import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.mappings.foundation.AbstractTransformationMapping;
import org.eclipse.persistence.mappings.transformers.AttributeTransformer;
import org.eclipse.persistence.sessions.Record;
import org.eclipse.persistence.sessions.Session;

public class DateAttributeTransformer implements AttributeTransformer {

private AbstractTransformationMapping mapping;
private SimpleDateFormat yyyyMMddHHmmss = new SimpleDateFormat("yyyyMMddHHmmss");

public void initialize(AbstractTransformationMapping mapping) {
this.mapping = mapping;
}

public Object buildAttributeValue(Record record, Object instance, Session session) {
try {
String dateString = null;
String timeString = null;

for (DatabaseField field : mapping.getFields()) {
if (field.getName().contains("DATE")) {
dateString = (String) record.get(field);
} else {
timeString = (String) record.get(field);
}
}
return yyyyMMddHHmmss.parseObject(dateString + timeString);
} catch(ParseException e) {
throw new RuntimeException(e);
}
}

}

8.7.2 FieldTransformerの使用

FieldTransformerを使用してJavaオブジェクトからXMLフィールド値を構成します。

各トランスフォーメーション・マッピングが複数の書込みトランスフォーマを持つ場合があります。この例では、次の2つが必要です。

- 1番目の書込みトランスフォーマは、yyMMdd形式で年、月および日を書き込みます。

例8-40 1番目の書込みトランスフォーマ

package example;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.eclipse.persistence.mappings.foundation.AbstractTransformationMapping;
import org.eclipse.persistence.mappings.transformers.FieldTransformer;
import org.eclipse.persistence.sessions.Session;

public class DateFieldTransformer implements FieldTransformer {

private AbstractTransformationMapping mapping;
private SimpleDateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");

public void initialize(AbstractTransformationMapping mapping) {
this.mapping = mapping;
}

public Object buildFieldValue(Object instance, String xPath, Session session) {
Date date = (Date) mapping.getAttributeValueFromObject(instance);
return yyyyMMdd.format(date);
}

}

- 2番目の書込みトランスフォーマは、HHmmss形式で時間、分および秒を書き込みます。

例8-41 2番目の書込みトランスフォーマ

package example;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.eclipse.persistence.mappings.foundation.AbstractTransformationMapping;
import org.eclipse.persistence.mappings.transformers.FieldTransformer;
import org.eclipse.persistence.sessions.Session;

public class TimeFieldTransformer implements FieldTransformer {

private AbstractTransformationMapping mapping;
private SimpleDateFormat HHmmss = new SimpleDateFormat("HHmmss");

public void initialize(AbstractTransformationMapping mapping) {
this.mapping = mapping;
}

public Object buildFieldValue(Object instance, String xPath, Session session) {
Date date = (Date) mapping.getAttributeValueFromObject(instance);
return HHmmss.format(date);
}

}

8.8 XMLスキーマからのJavaクラスの生成

JAXBコンパイラを使用してXMLスキーマからJavaクラスを生成します。生成したクラスにはXMLバインディング・メタデータを表すJAXB注釈が含まれます。

8.8.1 JAXBコンパイラの実行

.shファイルまたは.cmdファイルを次のように使用して、JAXBコンパイラを実行します。

<ECLIPSELINK_HOME>/eclipselink/bin/jaxb-compiler.sh <source-file.xsd> [-options]

または

<ECLIPSELINK_HOME>\eclipselink\bin\jaxb-compiler.cmd <source-file.xsd> [-options]

JAXBコンパイラは次のオプションをサポートしています。

オプション

説明

-nv

入力スキーマの厳密な検証を実行しません。

-extension

ベンダー固有の拡張を許可し、JAXB仕様の付録E.2の互換性ルールに厳密には従いません。

-b <file/directory>

処理する外部バインディング・ファイルを指定します。
注意: 各ファイルの独自の-bフラグを使用する必要があります。

-d <directory>

生成されるファイルの出力ディレクトリを指定します。

-p <package>

ターゲット・パッケージを指定します。

-classpath <arg>

ユーザー・クラス・ファイルを検索する場所を指定します。

-verbose

情報メッセージなどの追加コンパイラ出力を有効にします。

-quiet

コンパイラ出力を無効にします。

-version

コンパイラのバージョンを表示します。

次に例を示します。

jaxb-compiler.sh -d jaxb-compiler-output config/Customer.xsd

コンパイラ・オプションの完全なリストを表示するには、次を使用します。

jaxb-compiler.sh -help

8.9 生成されたマッピングのカスタマイズ

XMLスキーマ(sessions.xmlからのEclipseLinkプロジェクト)からのブートストラップ時に、EclipseLink OXMバインディング・ファイルを使用してEclipseLinkが生成するマッピングをカスタマイズできます。このファイルには追加のマッピングが含まれ、OXMとXSDブートストラップを組み合せることができます。つまり、EclipseLinkマッピングを使用して既存のXMLスキーマをカスタマイズできます。

この項では、スキーマに定義されたマッピングをオーバーライドする方法を示します。スキーマでは住所をカナダ式(provinceおよびpostal code)で定義していますが、米国式(stateおよびzip code)の住所を含むXMLを使用できます。

最初に、マッピング・オーバーライドを含んだeclipselink-oxm.xmlファイルを作成する必要があります。例8-42では、provinceとpostalCodeのXPathを変更します。

例8-42 eclipselink-oxm.xmlファイルのサンプル

<?xml version="1.0" encoding="US-ASCII"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm" package-name="example">
<java-types>
<java-type name="Address">
<java-attributes>
<xml-element java-attribute="province" xml-path="state/text()"/>
<xml-element java-attribute="postalCode" xml-path="zip-code/text()"/>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>

DynamicJAXBContextの作成時に、プロパティの引数を使用してこのバインディング・ファイルを(スキーマに加えて)DynamicJAXBContextFactoryに渡します。

// Load Schema
InputStream xsdStream = myClassLoader.getSystemResourceAsStream("example/resources/xsd/customer.xsd");

// Load OXM with customizations, put into Properties
InputStream oxmStream = myClassLoader.getSystemResourceAsStream("example/resources/eclipselink/eclipselink-oxm.xml");
Map<String, Object> props = new HashMap<String, Object>();
props.put(JAXBContextProperties.OXM_METADATA_SOURCE, oxmStream);

// Create Context
DynamicJAXBContext dContext = DynamicJAXBContextFactory.createContextFromXSD(inputStream, null, myClassLoader, props);

9 動的JAXBの使用

この章の内容は次のとおりです。

- 静的エンティティと動的エンティティの理解

- EclipseLinkランタイムの指定

- XMLスキーマ(XSD)からのブートストラップ

- EclipseLinkメタデータ(OXM)からのブートストラップ

9.1 静的エンティティと動的エンティティの理解

EclipseLink JAXBの高レベルな使用方法には、既存のJavaクラスの使用(「静的MOXyの使用」)とEclipseLinkで生成したメモリー内のJavaクラスの使用(「動的MOXyの使用」)の2つがあります。

9.1.1 静的MOXyの使用

EclipseLink JAXBの最も一般的な使用方法は、Java注釈および/またはEclipseLink OXMメタデータによってXMLにマップされた既存のJavaクラスの使用です。これらのクラスは、ユーザーが自分で記述したものか、XJCコンパイラ・ツールでXMLスキーマから生成したものです。

このアプローチを使用して、XMLへの変換およびXMLからの変換において実際のドメイン・オブジェクトを処理します。例9-1に、JAXBで使用できる単純なJavaクラスを示します。

例9-1 Javaクラスのサンプル

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Customer {
@XmlAttribute
private long id;

private String name;

// ...
// get() and set() methods
// ...
}

注意:
JAXBで静的クラスを使用する場合は、JAXBのデフォルトの動作を利用し、デフォルトとは異なる部分にのみ注釈を付けることができます。たとえば、JavaクラスのすべてのフィールドはデフォルトでXML要素にマップされるため、nameフィールドへの注釈付けは不要です。ただし、XML属性にマップするためにidフィールドが必要なため、そのように注釈付けしています。

例9-2に、静的JAXBを使用してオブジェクトをアンマーシャリング、変更およびマーシャリングする方法を示します。

例9-2 マーシャリングおよびアンマーシャリングの例

JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class, Address.class);
Customer customer = (Customer) jaxbContext.createUnmarshaller().unmarshal(instanceDoc);

Address address = new Address();
address.setStreet("1001 Fleet St.");

customer.setAddress(address);

jaxbContext.createMarshaller().marshal(customer, System.out);

9.1.2 動的MOXyの使用

EclipseLinkの動的MOXyでは、JavaクラスパスにJavaクラス・ファイルをコンパイルしなくても、様々なメタデータ・ソースからJAXBContextをブートストラップでき、既存のJAXB APIを使用してデータのマーシャリングおよびアンマーシャリングを行えます。これにより、必要に応じて以前に生成されたJavaソース・コードを更新および再コンパイルしなくてもメタデータを更新できます。

次の場合は、動的MOXyを検討してください。

- EclipseLinkでXMLスキーマ(XSD)からマッピングを生成する場合。

- 具体的なJavaドメイン・クラスを扱わない場合。

9.1.2.1 動的エンティティの使用

実際のJavaクラス(Customer.classやAddress.classなど)を使用するかわりに、動的MOXyでは単純なget(propertyName)/set(propertyName, propertyValue) APIを使用してデータを操作します。EclipseLinkは、(メモリー内に)各DynamicEntityに関連付けられたDynamicTypeを生成します。

注意:
DynamicTypesはJavaクラスと類似しています。一方、DynamicEntitiesはDynamicTypeのインスタンスとみなすことができます。

例9-3に、動的JAXBを使用してオブジェクトをアンマーシャリング、変更およびマーシャリングする方法を示します。

例9-3 マーシャリングおよびアンマーシャリングの例

DynamicJAXBContext dynamicJAXBContext = DynamicJAXBContextFactory.createContextFromXSD(xsdInputStream, null, myClassLoader, null);
DynamicEntity customer = (DynamicEntity) dynamicJAXBContext.createUnmarshaller().unmarshal(instanceDoc);

String lastName = customer.get("lastName");
List orders = customer.get("orders");
...
DynamicEntity address = dynamicJAXBContext.newDynamicEntity("mynamespace.Address");
address.set("street", "1001 Fleet St.");

customer.set("lastName", lastName + "Jr.");
customer.set("address", address);

dynamicJAXBContext.createMarshaller().marshal(customer, System.out);

注意:
メタデータのXML名(複合型名、要素名、属性名)は、『Java Architecture for XML Binding (JAXB) 2.2 Specification』(http://jcp.org/en/jsr/detail?id=222)の「Appendix D: Binding XML Names to Java Identifiers」で説明しているアルゴリズムに従ってJava識別子に変換されます。
例9-3では、XML内のlast-nameはJavaオブジェクト用にlastNameに変換されます。

9.2 EclipseLinkランタイムの指定

EclipseLink動的MOXyをJAXB実装として使用するには、jaxb.propertiesファイル内でEclipseLink DynamicJAXBContextFactoryを特定する必要があります。

1. jaxb.propertiesという名前のテキスト・ファイルを作成し、新しいJAXBContextsの構築に使用されるファクトリとして、DynamicJAXBContextFactoryを次のように指定します。
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContextFactory

2. JAXBContextの作成で使用されるコンテキスト・パスにjaxb.propertiesファイルをコピーします。

3. 標準のJAXBContext.newInstance(String contextPath) APIを使用してDynamicJAXBContextを作成します。
DynamicJAXBContext jaxbContext = (DynamicJAXBContext) JAXBContext.newInstance("org.example.mypackage");

アプリケーション・コードを変更する必要がないため、異なるJAXB実装間を容易に切り替えることができます。

9.2.1 DynamicJAXBContextのインスタンス化

JAXBContextで次のメソッドを使用して、DynamicJAXBContextsの新しいインスタンスを作成できます。

public static JAXBContext newInstance(String contextPath) throws JAXBException
public static JAXBContext newInstance(String contextPath, ClassLoader classLoader) throws JAXBException
public static JAXBContext newInstance(String contextPath, ClassLoader classLoader, Map<String,?> properties) throws JAXBException

- contextPath: jaxb.propertiesファイルの場所。

- classLoader: アプリケーションの現在のクラス・ローダー。新しいDynamicTypesが生成される前に、クラスの存在を確認するために最初にクラスを検索する際に使用されます。

- properties: 新しいDynamicJAXBContextの作成に使用されるプロパティのマップ。このマップには、次の2つのキーのいずれか1つが含まれている必要があります。

- 複数の値を取れるorg.eclipse.persistence.jaxb.JAXBContextFactory.XML_SCHEMA_KEY。

- 次のいずれかで、XMLスキーマ・ファイルを指すもの。

- java.io.InputStream

- org.w3c.dom.Node

- javax.xml.transform.Source

- 複数の値を取れるorg.eclipse.persistence.jaxb.JAXBContextProperties.OXM_METADATA_SOURCE。

- 次のいずれかで、OXMファイルを指すもの。

- java.io.File

- java.io.InputStream

- java.io.Reader

- java.net.URL

- javax.xml.stream.XMLEventReader

- javax.xml.stream.XMLStreamReader

- javax.xml.transform.Source

- org.w3c.dom.Node

- org.xml.sax.InputSource

注意:
これらのオプションの1つを使用する場合、OXMファイルのxml-bindings要素でパッケージ名要素が定義される必要があります。

- 前述のセットからのオブジェクトのリスト。

注意:
このオプションを使用する場合、OXMファイルのxml-bindings要素でパッケージ名要素が定義される必要があります。

- Map<String, Object>。ここで、Stringはパッケージ名で、Objectは前述の可能な値セットからOXMファイルへのポインタです。

9.3 XMLスキーマ(XSD)からのブートストラップ

EclipseLink MOXyを使用して、既存のXMLスキーマからDynamicJAXBContextを作成できます。EclipseLinkはスキーマを解析し、各複合型に対してDynamicTypesを生成します。これは、DynamicJAXBContextFactoryクラスの使用により実現されます。DynamicJAXBContextは直接インスタンス化できません。ファクトリAPIを介して作成する必要があります。

次のものを使用して、XMLスキーマをDynamicJAXBContextFactoryに渡すことができます。

- java.io.InputStream

- org.w3c.dom.Node

- javax.xml.transform.Source

注意:
EclipseLink MOXyは、SunのXJC (XMLからJavaへのコンパイラ)APIを使用してスキーマをメモリー内表現に解析し、動的タイプとマッピングを生成します。XSDからブートストラップするときは、CLASSPATHに(JAXBリファレンス実装から)jaxb-xjc.jarを含める必要があります。

DynamicJAXBContextの作成に使用するAPIは次のとおりです。

例9-4 DynamicJAXBContextの作成

/**
* Create a DynamicJAXBContext, using XML Schema as the metadata source.
*
* @param schemaStream
*      java.io.InputStream from which to read the XML Schema.
* @param resolver
*      An org.xml.sax.EntityResolver, used to resolve schema imports.  Can be null.
* @param classLoader
*      The application's current class loader, which will be used to first lookup
*      classes to see if they exist before new DynamicTypes are generated.  Can be
*      null, in which case Thread.currentThread().getContextClassLoader() will be used.
* @param properties
*      Map of properties to use when creating a new DynamicJAXBContext.  Can be null.
*
* @return
*      A new instance of DynamicJAXBContext.
*
* @throws JAXBException
*      if an error was encountered while creating the DynamicJAXBContext.
*/
public static DynamicJAXBContext createContextFromXSD(java.io.InputStream schemaStream, EntityResolver resolver,
ClassLoader classLoader, Map<String, ?> properties) throws JAXBException

public static DynamicJAXBContext createContextFromXSD(org.w3c.dom.Node schemaDOM, EntityResolver resolver,
ClassLoader classLoader, Map<String, ?> properties) throws JAXBException

public static DynamicJAXBContext createContextFromXSD(javax.xml.transform.Source schemaSource, EntityResolver resolver,
ClassLoader classLoader, Map<String, ?> properties) throws JAXBException

注意:
classLoaderパラメータは、アプリケーションの現在のクラス・ローダーです。新しいDynamicTypesが生成される前に、クラスの存在を確認するために最初にクラスを検索する際に使用されます。ユーザーがこのパラメータにnullを渡した場合は、かわりにThread.currentThread().getContextClassLoader()が使用されます。

この例では、動的MOXyを使用して新しいオブジェクトを作成およびマーシャリングする方法を示します。

例9-5 XMLスキーマのサンプル

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="example" xmlns:myns="example" xmlns:xs="http://www.w3.org/2001/XMLSchema"
attributeFormDefault="qualified" elementFormDefault="qualified">

<xs:element name="customer" type="myns:customer"/>

<xs:complexType name="customer">
<xs:sequence>
<xs:element name="first-name" type="xs:string"/>
<xs:element name="last-name" type="xs:string"/>
<xs:element name="address" type="myns:address"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="address">
<xs:sequence>
<xs:element name="street" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="province" type="xs:string"/>
<xs:element name="postal-code" type="xs:string"/>
</xs:sequence>
</xs:complexType>

</xs:schema>

例9-6のコード・スニペットは次のとおりです。

- DynamicJAXBContextを作成するため、XMLスキーマをDynamicJAXBContextFactoryに渡します。

- 新しいDynamicEntitiesを作成してプロパティを設定します。

- JAXBMarshallerを作成してJavaオブジェクトをXMLにマーシャリングします。

例9-6 アプリケーション・コードのサンプル

InputStream inputStream = myClassLoader.getSystemResourceAsStream("example/resources/xsd/customer.xsd");
DynamicJAXBContext dContext = DynamicJAXBContextFactory.createContextFromXSD(inputStream, null, myClassLoader, null);

DynamicEntity newCustomer = dContext.newDynamicEntity("example.Customer");
newCustomer.set("firstName", "George");
newCustomer.set("lastName", "Jones");

DynamicEntity newAddress = dContext.newDynamicEntity("example.Address");
newAddress.set("street", "227 Main St.");
newAddress.set("city", "Toronto");
newAddress.set("province", "Ontario");
newAddress.set("postalCode", "M5V1E6");

newCustomer.set("address", newAddress);

dContext.createMarshaller().marshal(newCustomer, System.out);

9.3.1 他のスキーマ/EntityResolverのインポート

ブートストラップに使用するXMLスキーマが別のスキーマをインポートする場合、org.xml.sax.EntityResolverを構成してインポートされたスキーマの場所を解決する必要があります。これで、EntityResolverをDynamicJAXBContextFactoryに渡すことができます。

例9-7では、各タイプは各自のスキーマで定義されます。

例9-7 XMLスキーマのサンプル

<!-- customer.xsd -->

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:myns="example" xmlns:add="addressNamespace"
xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="example">

<xs:import namespace="addressNamespace" schemaLocation="address.xsd"/>

<xs:element name="customer" type="myns:customer"/>

<xs:complexType name="customer">
<xs:sequence>
<xs:element name="first-name" type="xs:string"/>
<xs:element name="last-name" type="xs:string"/>
<xs:element name="address" type="add:address"/>
</xs:sequence>
</xs:complexType>

</xs:schema>

EntityResolver実装を指定してインポートされたスキーマの場所を解決する必要があります。

例9-8にEntityResolverを示します。

例9-8 アプリケーション・コードのサンプル

class MyEntityResolver implements EntityResolver {

public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
// Imported schemas are located in ext\appdata\xsd\

// Grab only the filename part from the full path
String filename = new File(systemId).getName();

// Now prepend the correct path
String correctedId = "ext/appdata/xsd/" + filename;

InputSource is = new InputSource(ClassLoader.getSystemResourceAsStream(correctedId));
is.setSystemId(correctedId);

return is;
}

}

次に示すように、DynamicJAXBContextの作成時に、EntityResolverを渡します。

InputStream inputStream = ClassLoader.getSystemResourceAsStream("com/foo/sales/xsd/customer.xsd");
DynamicJAXBContext dContext = DynamicJAXBContextFactory.createContextFromXSD(inputStream, new MyEntityResolver(), null, null);

別のスキーマをインポートする際に、次の例外が表示される場合があります。

Internal Exception: org.xml.sax.SAXParseException: schema_reference.4: Failed to read schema document '<imported-schema-name>', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not <xsd:schema>.

XJCのスキーマの正確性チェック・オプションを次のコードで無効にします。

System.setProperty("com.sun.tools.xjc.api.impl.s2j.SchemaCompilerImpl.noCorrectnessCheck", "true")

または、コマンドラインから次を入力します。

-Dcom.sun.tools.xjc.api.impl.s2j.SchemaCompilerImpl.noCorrect

9.3.2 XJC外部バインディング・カスタマイズ・ファイルで生成されたマッピングのカスタマイズ

XSDからブートストラップするときに、オプションでXJCの外部バインディング・カスタマイズ・ファイル形式(.xjb)を使用して生成されるマッピングをカスタマイズできます。次の例では、動的クラスのパッケージ名がオーバーライドされ、name属性はlast-name-comma-first-nameに名前変更されます。

例9-9 custom1.xjbファイル

<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="employee.xsd" node="/xs:schema">

<!-- Customize the package name that is generated for each schema -->
<jxb:schemaBindings>
<jxb:package name="com.acme.internal"/>
</jxb:schemaBindings>

<!-- Rename the 'name' element to 'last-name-comma-first-name' -->
<jxb:bindings node="//xs:complexType[@name='person']">
<jxb:bindings node=".//xs:element[@name='name']">
<jxb:property name="last-name-comma-first-name"/>
</jxb:bindings>
</jxb:bindings>

</jxb:bindings>
</jxb:bindings>

外部バインディング・カスタマイズ・ファイル形式の詳細は、http://download.oracle.com/docs/cd/E17802_01/webservices/webservices/docs/2.0/tutorial/doc/JAXBUsing4.htmlを参照してください。

注意:
外部バインディング・カスタマイズ・ファイルを使用する場合、XMLスキーマを指すSourceオブジェクトを使用する必要があります。Sourcesは.xjbファイルのロードにも使用され、すべて同一のシステムIDセットを持つ必要があります。

例9-10に、XSDからのブートストラップと、2つの別個の.xjbファイルを使用したマッピング生成のカスタマイズを示します。

例9-10 ブートストラップの例

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
String xsd = "example/resources/xsd/employee.xsd";
String xjb1 = "example/resources/xsd/custom1.xjb";
String xjb2 = "example/resources/xsd/custom2.xjb";

InputStream xsdStream = classLoader.getSystemResourceAsStream(xsd);
Source xsdSource = new StreamSource(xsdStream);
// Set SYSTEM_ID to the filename part of the XSD
xsdSource.setSystemId("employee.xsd");

InputStream xjbStream = classLoader.getResourceAsStream(xjb1);
Source xjbSource = new StreamSource(xjbStream);
// Set SYSTEM_ID to be the same as the XSD
xjbSource.setSystemId(xsdSource.getSystemId());

InputStream xjbStream2 = classLoader.getResourceAsStream(xjb2);
Source xjbSource2 = new StreamSource(xjbStream2);
// Set SYSTEM_ID to be the same as the XSD
xjbSource2.setSystemId(xsdSource.getSystemId());

ArrayList<Source> xjbFiles = new ArrayList<Source>(2);
xjbFiles.add(xjbSource);
xjbFiles.add(xjbSource2);

// Put XSD and XJBs into Properties
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(DynamicJAXBContextFactory.XML_SCHEMA_KEY, xsdSource);
properties.put(DynamicJAXBContextFactory.EXTERNAL_BINDINGS_KEY, xjbFiles);

// Create Context
DynamicJAXBContext jaxbContext = (DynamicJAXBContext) JAXBContext.newInstance("example", classLoader, properties);

EXTERNAL_BINDINGS_KEYの値は、単一のSourceまたはList<Source>のいずれかで、外部バインディング・カスタマイズ・ファイルを指しています。

9.4 EclipseLinkメタデータ(OXM)からのブートストラップ

XMLへのDynamicEntitiesのマッピングをさらに制御するには、かわりにEclipseLink OXMメタデータ・ファイルからブートストラップできます。このアプローチを使用すると、EclipseLinkの堅牢なマッピング・フレームワークを活用でき、XMLの複合型をそれぞれ対応するJavaにマップする方法をカスタマイズできます。次のDynamicJAXBContextFactoryのAPIを使用して、OXMファイルからブートストラップできます。

例9-11 DynamicJAXBContextの作成

/**
* Create a <tt>DynamicJAXBContext</tt>, using an EclipseLink OXM file as the metadata source.
*
* @param classLoader
*      The application's current class loader, which will be used to first lookup classes to
*      see if they exist before new <tt>DynamicTypes</tt> are generated. Can be <tt>null</tt>,
*      in which case <tt>Thread.currentThread().getContextClassLoader()</tt> will be used.
* @param properties
*      Map of properties to use when creating a new <tt>DynamicJAXBContext</tt>.  This map must
*      contain a key of JAXBContext.ECLIPSELINK_OXM_XML_KEY, with a value of... (see below)
*
* @return
*      A new instance of <tt>DynamicJAXBContext</tt>.
*
* @throws JAXBException
*      if an error was encountered while creating the <tt>DynamicJAXBContext</tt>.
*/
public static DynamicJAXBContext createContextFromOXM(ClassLoader classLoader, Map<String, ?> properties) throws JAXBException {

実際のOXMファイルへのリンクは、特殊キーJAXBContextProperties.OXM_METADATA_SOURCEを使用し、propertiesパラメータを介して渡されます。このキーの値は、次のいずれかの形式によるOXMメタデータ・ファイルへのハンドルとなります。

- java.io.File

- java.io.InputStream

- java.io.Reader

- java.net.URL

- javax.xml.stream.XMLEventReader

- javax.xml.stream.XMLStreamReader

- javax.xml.transform.Source

- org.w3c.dom.Node

- org.xml.sax.InputSource

複数のOXMファイルからブートストラップするために、前述の入力のListsも受け入れられます。詳細は、DynamicJAXBContextFactoryクラスのドキュメントを参照してください。

次の例では、ClassLoaderからリソースとしてOXMファイルを取得し、結果のInputStreamを使用してDynamicJAXBContextをブートストラップします。

InputStream iStream = myClassLoader.getResourceAsStream("example/resources/eclipselink/eclipselink-oxm.xml");

Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, iStream);

DynamicJAXBContext jaxbContext = DynamicJAXBContextFactory.createContextFromOXM(myClassLoader, properties);

9.4.1 例

例9-12のOXMのサンプルを使用して、動的MOXyを使用して新しいオブジェクトを作成およびマーシャリングする方法を示します。type属性をメモしておくことが大切です。基礎となるJavaクラスがないため、各プロパティのタイプを明示的に指定する必要があるからです。

例9-12 XMLスキーマのサンプル

<?xml version="1.0" encoding="US-ASCII"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xs="http://www.w3.org/2001/XMLSchema" package-name="example">

<java-types>
<java-type name="Customer">
<xml-root-element name="customer"/>
<java-attributes>
<xml-element java-attribute="firstName" type="java.lang.String"/>
<xml-element java-attribute="lastName" type="java.lang.String"/>
<xml-element java-attribute="address" type="example.Address"/>
</java-attributes>
</java-type>

<java-type name="Address">
<java-attributes>
<xml-element java-attribute="street" type="java.lang.String"/>
<xml-element java-attribute="city" type="java.lang.String"/>
<xml-element java-attribute="province" type="java.lang.String"/>
<xml-element java-attribute="postalCode" type="java.lang.String"/>
</java-attributes>
</java-type>
</java-types>

</xml-bindings>

例9-13のコードは、次のことを示しています。

- DynamicJAXBContextを作成するため、OXMファイルをDynamicJAXBContextFactoryに渡します。

- 新しいDynamicEntitiesを作成してプロパティを設定します。

- JAXBMarshallerを作成してJavaオブジェクトをXMLにマーシャリングします。

例9-13 アプリケーション・コードのサンプル

InputStream iStream = myClassLoader.getResourceAsStream("example/resources/eclipselink/eclipselink-oxm.xml");

Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, iStream);

DynamicJAXBContext jaxbContext = DynamicJAXBContextFactory.createContextFromOXM(myClassLoader, properties);

DynamicEntity newCustomer = dContext.newDynamicEntity("example.Customer");
newCustomer.set("firstName", "George");
newCustomer.set("lastName", "Jones");

DynamicEntity newAddress = dContext.newDynamicEntity("example.Address");
newAddress.set("street", "227 Main St.");
newAddress.set("city", "Toronto");
newAddress.set("province", "Ontario");
newAddress.set("postalCode", "M5V1E6");

newCustomer.set("address", newAddress);

dContext.createMarshaller().marshal(newCustomer, System.out

10 JSONドキュメントの使用

リリース2.4以降のEclipseLink MOXyでは、オブジェクトからJSONへの変換およびJSONからオブジェクトの変換を行う機能をサポートしています(JavaScriptオブジェクト表記)。この機能はRESTfulサービスを作成する際に役立ちます。JAX-RSサービスはXMLおよびJSONメッセージの両方を受け入れます。

この章の内容は次のとおりです。

- JSONドキュメントの理解

- JSONドキュメントのマーシャリングおよびアンマーシャリング

- JSONバインディングの指定

10.1 JSONドキュメントの理解

EclipseLinkは、次のようなJSONの読取りおよび書込みを行う際のすべてのMOXyオブジェクトとXML間のオプションをサポートしています。

- (JAXB仕様に加えて)EclipseLinkの高度で拡張されたマッピング機能

- 外部バインディング・ファイルのマッピングの格納

- 動的JAXBでの動的モデルの作成

- マルチテナント・アプリケーションをサポートする拡張可能モデルの構築

10.2 JSONドキュメントのマーシャリングおよびアンマーシャリング

例10-1に示すように、JAXBマーシャラまたはJAXBアンマーシャラのeclipselink.media-typeプロパティを使用して、アプリケーションでJSONドキュメントを生成および使用します。

例10-1 マーシャリングおよびアンマーシャリング

...

Marshaller m = jaxbContext.createMarshaller();
m.setProperty("eclipselink.media-type", "application/json");

Unmarshaller u = jaxbContext.createUnmarshaller();
u.setProperty("eclipselink.media-type", "application/json");

...

例10-2に示すように、JAXBContextを作成する際に、使用されるプロパティのMapにeclipselink.media-typeプロパティを指定できます。

例10-2 マップの使用

import org.eclipse.persistence.jaxb.JAXBContextProperties;
import org.eclipse.persistence.oxm.MediaType;

Map<String, Object> properties = new HashMap<String, Object>();
properties.put("eclipselink.media-type", "application/json");

JAXBContext ctx = JAXBContext.newInstance(new Class[] { Employee.class }, properties);
Marshaller jsonMarshaller = ctx.createMarshaller();
Unmarshaller jsonUnmarshaller = ctx.createUnmarshaller();

マップに指定すると、JAXBContentから作成されたマーシャラおよびアンマーシャラは、自動的に指定したメディア・タイプを使用します。

また、例10-3に示すように、MarshallerProperties、UnmarshallerPropertiesおよびMediaType定数を使用してJSONドキュメントを使用するようアプリケーションを構成できます。

例10-3 MarshallerPropertiesおよびUnarshallerPropertiesの使用

import org.eclipse.persistence.jaxb.MarshallerProperties;
import org.eclipse.persistence.jaxb.UnarshallerProperties;
import org.eclipse.persistence.oxm.MediaType;

m.setProperty(MarshallerProperties.MEDIA_TYPE, MediaType.APPLICATION_JSON);
u.setProperty(UnmarshallerProperties.MEDIA_TYPE, MediaType.APPLICATION_JSON);
...

10.3 JSONバインディングの指定

例10-4に、通常のJAXBの使用で必要となる依存性やコンパイル時の依存性を必要としない、基本的なJSONバインディングを示します。この例では、StreamSourceからユーザー・オブジェクトSearchResultsにJSONをアンマーシャリングし、新しいResultをコレクションに追加し、その後新しいコレクションをSystem.outに追加する方法を示します。

例10-4 基本的なJSONバインディングの使用

package org.example;

import org.example.model.Result;
import org.example.model.SearchResults;

import java.util.Date;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;

public class Demo {

public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(SearchResults.class);

Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setProperty("eclipselink.media-type", "application/json");
StreamSource source = new StreamSource("http://search.twitter.com/search.json?q=jaxb");
JAXBElement<SearchResults> jaxbElement = unmarshaller.unmarshal(source, SearchResults.class);

Result result = new Result();
result.setCreatedAt(new Date());
result.setFromUser("bsmith");
result.setText("You can now use EclipseLink JAXB (MOXy) with JSON :)");
jaxbElement.getValue().getResults().add(result);

Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty("eclipselink.media-type", "application/json");
marshaller.marshal(jaxbElement, System.out);
}

}

また、MOXy外部バインディング・ファイルをJSONドキュメントとして書き込むこともできます。例10-5に、bindings.jsonを使用してCustomerクラスおよびPhoneNumberクラスをJSONにマップする方法を示します。

例10-5 外部バインディングの使用

{
"package-name" : "org.example",
"xml-schema" : {
"element-form-default" : "QUALIFIED",
"namespace" : "http://www.example.com/customer"
},
"java-types" : {
"java-type" : [ {
"name" : "Customer",
"xml-type" : {
"prop-order" : "firstName lastName address phoneNumbers"
},
"xml-root-element" : {},
"java-attributes" : {
"xml-element" : [
{"java-attribute" : "firstName","name" : "first-name"},
{"java-attribute" : "lastName", "name" : "last-name"},
{"java-attribute" : "phoneNumbers","name" : "phone-number"}
]
}
}, {
"name" : "PhoneNumber",
"java-attributes" : {
"xml-attribute" : [
{"java-attribute" : "type"}
],
"xml-value" : [
{"java-attribute" : "number"}
]
}
} ]
}
}

例10-6に、JAXBContextをブートストラップする際に(例10-5で作成した)JSONファイルを使用する方法を示します。

例10-6 JSONを使用したJAXBContextのブートストラップ

Map<String, Object> properties = new HashMap<String, Object>(2);
properties.put("eclipselink-oxm-xml", "org/example/binding.json");
properties.put("eclipselink.media-type", "application/json");
JAXBContext context = JAXBContext.newInstance("org.example", Customer.class.getClassLoader() , properties);

Unmarshaller unmarshaller = context.createUnmarshaller();
StreamSource json = new StreamSource(new File("src/org/example/input.json"));
...

10.3.1 JSONデータ型の指定

XMLでは単一のデータ型を持ちますが、JSONでは文字列、数値およびブールを区別します。例10-7に示すように、EclipseLinkはこれらのデータ型を自動的にサポートします。

例10-7 JSONデータ型の使用

public class Address {

private int id;
private String city;
private boolean isMailingAddress;

}

{
"id" : 1,
"city" : "Ottawa",
"isMailingAddress" : true
}

10.3.2 属性のサポート

JSONでは属性を使用しません。@XmlAttribute注釈でマップされたものはすべて要素としてマーシャリングされます。デフォルトでは、EclipseLinkは属性と要素のイベントの両方をトリガーします。そのため、マップされた属性または要素のいずれかが値を処理できます。

この動作をオーバーライドするには、例10-8に示すように、JSON_ATTRIBUTE_PREFIXプロパティを使用して属性の接頭辞を指定します。EclipseLinkは、マーシャリング時に属性名に接頭辞を付加して、アンマーシャリング時にそれを識別します。

次の例では、numberフィールドが接頭辞@を持つ属性としてマップされます。

例10-8 接頭辞の使用

jsonUnmarshaller.setProperty(UnmarshallerProperties.JSON_ATTRIBUTE_PREFIX, "@");
jsonMarshaller.setProperty(MarshallerProperties.JSON_ATTRIBUTE_PREFIX, "@") ;

{
"phone" : {
"area-code" : "613",
"@number" : "1234567"
}
}

また、例10-9に示すように、JAXBContextを作成する際に、使用されるMapにJSON_ATTRIBUTE_PREFIXプロパティを設定できます。コンテキストから作成されたすべてのマーシャラおよびアンマーシャラで、指定された接頭辞を使用します。

例10-9 Mapへの接頭辞の設定

Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.JSON_ATTRIBUTE_PREFIX, "@");

JAXBContext ctx = JAXBContext.newInstance(new Class[] { Phone.class }, properties);

10.3.3 ルート要素なしのサポート

EclipseLinkは、ルート要素のないJSONドキュメントをサポートします。デフォルトでは、@XmlRootElement注釈が存在しない場合、マーシャリングされたJSONドキュメントはルート要素を持ちません。例10-10に示すように、ドキュメントのマーシャリング時にJSON_INCLUDE_ROOTプロパティを設定することで、この動作をオーバーライドできます(@XmlRootElementが指定されている場合でも、JSON出力からルート要素を省略します)。

例10-10 ルート要素のないドキュメントのマーシャリング

marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, false);

ルート要素のないドキュメントをアンマーシャリングする場合、例10-10に示すようにJSON_INCLUDE_ROOTプロパティを設定する必要があります。

例10-11 ルート要素のないドキュメントのアンマーシャリング

unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false);
JAXBElement<SearchResults> jaxbElement = unmarshaller.unmarshal(source, SearchResults.class);

注意:
ドキュメントにルート要素がない場合、アンマーシャリングするクラスを指定する必要があります。

10.3.4 ネームスペースの使用

JSONはネームスペースを持たないため、デフォルトですべてのネームスペースおよび接頭辞は、マーシャリングおよびアンマーシャリング時に無視されます。同一のローカル名に複数のマッピングを持つために、マッピングの区別ができないことによる問題である場合があります。

EclipseLinkでは、マーシャラおよびアンマーシャラに対してネームスペースと接頭辞間のマップ(NamespacePrefixMapperのインスタンス)を指定できます。ネームスペースの接頭辞は、要素名に付加されてマーシャリングされたドキュメントに表示されます。EclipseLinkは、アンマーシャリング操作時に接頭辞を識別し、結果のJavaオブジェクトは適切なネームスペースに配置されます。

例10-12に、NAMESPACE_PREFIX_MAPPERプロパティの使用方法を示します。

例10-12 ネームスペースの使用

Map<String, String> namespaces = new HashMap<String, String>();
namespaces.put("namespace1", "ns1");
namespaces.put("namespace2", "ns2");
jsonMarshaller.setProperty(MarshallerProperties.NAMESPACE_PREFIX_MAPPER, namespaces);
jsonUnmarshaller.setProperty(UnmarshallerProperties.JSON_NAMESPACE_PREFIX_MAPPER, namespaces);

MarshallerProperties.NAMESPACE_PREFIX_MAPPERはXMLおよびJSONの両方に適用されます。UnmarshallerProperties.JSON_NAMESPACE_PREFIX_MAPPERはJSONのみのプロパティです。XMLアンマーシャリングは、ネームスペース情報をドキュメントから直接取得できます。

JSONのマーシャリング時に、ネームスペースにマップからドット( . )で区切られた接頭辞が付与されます。

{
"ns1.employee : {
"ns2.id" : 123
}
}

ドット区切りは、JSON_NAMESPACE_SEPARATORプロパティを使用すると任意のカスタム文字に設定できます。ここでは、かわりにコロン( : )が使用されます。

jsonMarshaller.setProperty(MarshallerProperties.JSON_NAMESPACE_SEPARATOR, ':');
jsonUnmarshaller.setProperty(UnmarshallerProperties.JSON_NAMESPACE_SEPARATOR, ':');

10.3.5 コレクションの使用

例10-13に示すように、JSONへのマーシャリング時に、EclipseLinkはデフォルトで空のコレクションを[ ]としてマーシャリングします。

例10-13

{
"phone" : {
"myList" : [ ]
}
}

JSON_MARSHAL_EMPTY_COLLECTIONSプロパティを使用してこの動作をオーバーライドできます(空のコレクションは一切マーシャリングされません)。

jsonMarshaller.setProperty(MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS, Boolean.FALSE) ;

{
"phone" : {
}
}

10.3.6 ルート・レベル・コレクションのマッピング

@XmlRootElement(name="root")注釈を使用してルート・レベルを指定する場合、JSONドキュメントを次のようにマーシャリングできます。

marshaller.marshal(myListOfRoots, System.out);

[ {
"root" : {
"name" : "aaa"
}
}, {
"root" : {
"name" : "bbb"
}
} ]

ルート要素はドキュメント内に存在するため、次を使用してアンマーシャリングできます。

unmarshaller.unmarshal(json);

クラスが@XmlRootElementを持たない場合(またはJSON_INCLUDE_ROOT = falseの場合)、マーシャリングで次が生成されます。

[ {
"name":"aaa"
}, {
"name":"bbb"
} ]

ルート要素が存在しないため、次のようにアンマーシャリングするクラスを示す必要があります。

unmarshaller.unmarshal(json, Root.class);

10.3.7 XML値のラップ

例10-14に示すように、JAXBは@XmlValueクラスで1つ以上の@XmlAttributesをサポートしています。

例10-14 @XmlAttributesの使用

public class Phone {

@XmlValue
public String number;

@XmlAttribute
public String areaCode;

public Phone() {
this("", "");
}

public Phone(String num, String code) {
this.number = num;
this.areaCode = code;
}

}

有効なJSONドキュメントを生成するために、EclipseLinkは例10-15に示すようにvalueのラッパーを使用します。

例10-15 値ラッパーの使用

{
"employee" : {
"name" : "Bob Smith",
"mainPhone" : {
"areaCode" : "613",
"value" : "555-5555"
},
"otherPhones" : [ {
"areaCode" : "613",
"value" : "123-1234"
}, {
"areaCode" : "613",
"value" : "345-3456"
} ]
}
}

デフォルトでは、EclipseLinkはvalueをラッパーの名前として使用します。例10-16に示すように、JSON_VALUE_WRAPPERプロパティを使用して値ラッパーの名前をカスタマイズします。

例10-16

jsonMarshaller.setProperty(MarshallerProperties.JSON_VALUE_WRAPPER, "$");
jsonUnmarshaller.setProperty(UnmarshallerProperties.JSON_VALUE_WRAPPER, "$");

次が生成されます。

{
"employee" : {
"name" : "Bob Smith",
"mainPhone" : {
"areaCode" : "613",
"$" : "555-5555"
},
"otherPhones" : [ {
"areaCode" : "613",
"$" : "123-1234"
}, {
"areaCode" : "613",
"$" : "345-3456"
} ]
}
}

例10-17に示すように、JAXBContextを作成する際に、使用されるプロパティのMapにJSON_VALUE_WRAPPERプロパティを指定できます。

例10-17 マップの使用

Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.JSON_VALUE_WRAPPER, "$");

JAXBContext ctx = JAXBContext.newInstance(new Class[] { Employee.class }, properties);
Marshaller jsonMarshaller = ctx.createMarshaller();
Unmarshaller jsonUnmarshaller = ctx.createUnmarshaller();

マップに指定すると、JAXBContentから作成されたマーシャラおよびアンマーシャラは、自動的に指定した値ラッパーを使用します。