この章の内容は次のとおりです。
マップされたクラスのうち、少なくとも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オブジェクトのcontactInfo
プロパティに@XmlElementRef
という注釈を付けます。これによって、値タイプが要素名(およびネームスペースURI)から導出されることが示されます。
public class Customer {
private ContactInfo contactInfo;
@XmlElementRef
public ContactInfo getContactInfo() {
return contactInfo;
}
...
}
この手法を使用し、例にある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
が含まれています。