この章の内容は次のとおりです。
@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>
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を使用して適切な表現を選択します。
デフォルトでは、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;
}
}
次の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-2に、XML文書内のunionフィールドへのマッピングを示します。このXML文書はこの例のスキーマに準拠しています。EclipseLinkでXML文書をアンマーシャリングする場合、正常な変換が行われるまで、union型のそれぞれが試行されます。unionにおける最初のスキーマ・タイプはxsd:decimalです。10.5は有効な10進数であるため、EclipseLinkはその値を適切な型に変換します。
図5-3では、値Mは、有効なxsd:decimal型ではないため、次のunion型であるxsd:stringが試行されます。
現時点では、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);
}
}
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")となります。
byte[]やByte[]などのバイナリ型フィールドにマッピングする際には、これ以外にも検討すべき項目があります。
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);
他の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>
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>
バイナリ・データがインラインの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>