この章の内容は次のとおりです。
マップされたクラスのうち、少なくとも1つには、デフォルトのルート要素が定義されている必要があります。これにより、XML文書の最上位レベルのルートが何になるかがEclipseLinkに通知されます。図3-1に示すCustomerクラスとAddressクラスについて考えてみます。
これらのクラスは、例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-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」を参照してください。
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>
ほとんどのXML文書は、ネームスペースで修飾されています。Javaクラスの要素は、次の各レベルでネームスペースによって修飾できます。
ほとんどの場合は、パッケージ・レベルの注釈で十分です。他のレベルを使用してドキュメントをカスタマイズできます。ネームスペースを指定するには、@XmlSchema注釈を使用します。
パッケージに対して@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-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ネームスペースで修飾されています。
パッケージまたはタイプのネームスペースは、プロパティ/フィールド・レベルでオーバーライドできます。属性および要素の注釈はすべて、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ネームスペースで修飾されています。
EclipseLink MOXyには、次のように、XMLでの継承階層を表す手段がいくつか用意されています。
デフォルトでは、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属性に注目してください。
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要素にマーシャリングされていることに注意してください。
また、(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が含まれています。