This chapter includes the following sections:
You can use the @XmlSchemaType
annotation to customize the XML representation of date and time information. Additionally, EclipseLink MOXy supports the following types which are not covered in the JAXB specification (JSR-222):
java.sql.Date
java.sql.Time
java.sql.Timestamp
The following XML schema contains a date-of-birth element of type xsd:date
:
<?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>
The JAXB XML Schema to Java compiler (XJC) can be used to generate a class model from the sample schema. For example:
> xjc -d output-dir -p example date.xsd
will generate the following Customer class:
Example 5-2 Sample Customer Class
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; } }
Notice that:
The dateOfBirth
property is of type javax.xml.datatype.XMLGregorianCalendar
The dateOfBirth
property uses the @XmlSchemaType
annotation
Some Java data types (like XMLGregorianCalendar) have multiple XML representations (such as xsd:date
, xsd:time
, or xsd:dateTime). Use @XmlSchemaType
to select the appropriate representation.
By default, the JAXB XML schema to Java compiler (XJC) generates a property of type XMLGregorianCalendar
. However, you can easily change this to java.util.Date
or java.util.Calendar
, as shown in Example 5-3:
Example 5-3 Using 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; } }
The following XML schema and class diagram show a typical use of an XML Schema Union:
<?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>
Figure 5-2 illustrates a mapping to a union field in an XML document that conforms to the example schema. When EclipseLink unmarshalls the XML document, it tries each of the union types until it can make a successful conversion. The first schema type in the union is xsd:decimal
. Because 10.5 is a valid decimal, EclipseLink converts the value to the appropriate type.
Figure 5-2 Mapping to a Union Field in an XML Document
In Figure 5-3, the value M is not a valid xsd:decimal
type, so the next union type is tried, xsd:string
.
Currently, EclipseLink does not support the mapping of Unions using Annotations or OXM Metadata. However, an EclipseLink XML Customizer can be used to create the mapping.
First, we annotate the shoeSize
attribute with @XmlTransient
, to avoid automatically generating a mapping for it. We also include an @XmlCustomizer
annotation; the CustomerCustomizer
class will create the Union mapping in code.
Example 5-5 Using an EclipseLink Customizer
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;
...
}
The CustomerCustomizer
class can be used to manually add a mapping to the shoeSize
attribute. In Example 5-6, an XMLUnionField
is configured on the mapping, and the possible Union member types are added by calling addSchemaType()
:
Example 5-6 Mapping a Union Field
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);
}
}
The order of the calls to addSchemaType()
is important; when converting an XML value into Java, EclipseLink will attempt the conversions in the order that they were added to the field, and return as soon as a successful conversion is made. For example, when unmarshalling a shoeSize
of 10.5:
... shoeSizeField.addSchemaType(XMLConstants.DECIMAL_QNAME); shoeSizeField.addSchemaType(XMLConstants.STRING_QNAME); ...
A BigDecimal
will be created to store the value. If, however, your XMLUnionField was set up like this:
... shoeSizeField.addSchemaType(XMLConstants.STRING_QNAME); shoeSizeField.addSchemaType(XMLConstants.DECIMAL_QNAME); ...
The shoeSize value will be a String
("10.5").
EclipseLink uses a set of default conversions to create a value for the Java attribute (in this case, xsd:decimal
will be converted into a BigDecimal
). You can override this behavior in Java code using the XMLUnionField method addConversion. For example, if you want your Java object to store shoeSize
as a Float
:
shoeSizeField.addConversion(XMLConstants.DECIMAL_QNAME, Float.class);
There are additional items to consider when mapping to binary type fields, such as byte[]
or Byte[]
.
EclipseLink supports marshalling and unmarshalling binary data in two different representation formats: base64Binary
(default) and hexBinary
. You can specify the desired binary format using the @XmlSchemaType
annotation, or <xml-schema-type>
element in EclipseLink OXM. The examples below shows the result of marshalling the same byte[]
to each of these formats.
package example; import javax.xml.bind.annotation.*; @XmlRootElement public class BinaryData { @XmlSchemaType(name="hexBinary") public byte[] hexBytes; @XmlSchemaType(name="base64Binary") public byte[] base64Bytes; }
... <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);
Unlike other Java primitive/wrapper types, EclipseLink differentiates between byte[]
(primitive) and Byte[]
(wrapper) data types. By default, byte[]
will marshal to a single element or attribute, whereas Byte[]
will marshal each byte
as its own element, as illustrated by the following example:
Example 5-10 Using byte[] and 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);
<?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>
If you are using EclipseLink MOXy in a Web Services environment, certain types of binary data may be created as an MTOM/XOP Attachment, instead of written directly into an XML element or attribute. This is done as an optimization for large amounts of binary data.
The following table shows the Java types that are automatically treated as Attachments, along with their corresponding MIME type:
Table 5-1 Java Attributes Treated as Attachments
Java Type | MIME Type |
---|---|
java.awt.Image |
image/gif |
java.awt.Image |
image/jpeg |
javax.xml.transform.Source |
text/xml |
application/xml |
* |
javax.activation.DataHandler |
*/* |
Note:
For more information on the basics of SOAP Attachments, see "Appendix H: Enhanced Binary Data Handling" of the Java Architecture for XML Binding (JAXB) Specification (http://jcp.org/en/jsr/detail?id=222).
The following Java class contains two binary fields: a simple byte[]
, and a java.awt.Image
. In a Web Services environment, the Image data will automatically be created as an attachment.
Example 5-12 Sample Java Class
package example; import java.awt.Image; import javax.xml.bind.annotation.*; @XmlRootElement public class BinaryData { public byte[] bytes; public Image photo; }
Marshalling the object in Example 5-12 in a Web Services environment would look something like Example 5-13 (the actual appearance will depend on your application server's implementation of AttachmentMarshaller
):
<?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>
If you would like to force your binary data to be written as an inline string
in your XML, you can annotate the field with the @XmlInlineBinaryData
annotation:
Example 5-14 Using the @XmlInlineBinaryData Annotation
package example;
import java.awt.Image;
import javax.xml.bind.annotation.*;
@XmlRootElement
public class BinaryData {
public byte[] bytes;
@XmlInlineBinaryData
public Image photo;
}
This will result in an XML document like this:
<?xml version="1.0" encoding="UTF-8"?> <binaryData> <bytes>Ii04Q05ZWSIXDAwLAg==</bytes> <photo>/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHB ... Af/2Q==</photo> </binaryData>