JaVa
   

Customizing JAXB

Screenshot showed the use of custom binding declarations with the binding compiler, to override the default binding behavior. The binding declarations are XML elements themselves. JAXB specifies two techniques to pass them to the binding compiler:

When inlined, the declarations are included, using the xsd:annotation and xsd:appinfo tags. In XML schemas, the annotation element is a top-level element that specifies comments treated as inline documentation by schema parsers. The annotation may have any number of documentation or appInfo elements as child elements. appinfo specifies information specific to the app. A sample schema fragment is shown below:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:annotation>
 <xsd:appInfo>W3Schools Note</xsd:appInfo>
 <xsd:documentation xml:lang="en">
 Some app documentation here
 </xsd:documentation>
</xsd:annotation>
<!--other elements here with possibly any number of annotations-->
</xsd:schema>



When inlining declarations with the source schema, JAXB uses these annotation and appInfo elements to insert the JAXB specific information, using the following syntax:

<xsd:annotation>
 <xsd:appinfo>
 <binding declaration>
 </xsd:appinfo>
</xsd:annotation>


If an external file is used to specify the declarations, the file follows this format:

<jaxb:bindings schemaLocation = "location of schema here">
 <jaxb:bindings node = "XPath String referencing node here">
 <binding declaration>
 <jaxb:bindings>
</jaxb:bindings>


For example, an external binding declaration referencing the billingaddress node in the purchaseorder.xsd schema may look like this:

<jaxb:bindingsxmlns:jaxb="http://java.oracle.com/xml/jaxb"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns:ens="http://www.flutebank.com/schema" version="1.0">
 <jaxb:bindings schemaLocation="purchaseorder.xsd">
 <jaxb:binding node="./xs:attribute[@name='billingaddress']">
 <jaxb:property />
 </jaxb:binding>
 </jaxb:binding>
</jaxb:bindings>


A binding declaration has scope that specifies the schema elements to which it applies. There are four levels of scope, described below and shown in Screenshot, ranging from the generic to the specific. The more specific scope (e.g., component) overrides the higher-level (e.g., global) scope.

Java Click To expand
Screenshot: Declaration scope

Our treatment of the declarations in the subsequent paragraphs has purposely been kept light. We recommend the JAXB specifications for detailed syntax use, for two reasons:

JAXB defines seven distinct custom declarations that can be annotated to XML schemas. Let us look at an example of how custom bindings can be used with the purchaseorder.xsd schema in Listing 13.1. We will walk down the different steps and also the specifics of the syntax for the declarations used.

Including the JAXB Namespace

When using inlined annotations to specify the custom bindings, the XML schema must include the namespace and version number defined in JAXB. The modified declaration for the purchase order is shown below, with modifications highlighted in bold:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema targetNamespace="http://www.flutebank.com/schema"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns="http://www.flutebank.com/schema" elementFormDefault="qualified"
 xmlns:jaxb="http://java.oracle.com/xml/ns/jaxb" jaxb:version="1.0">


The globalBindings Declaration

The globalBindings declaration has global scope and is used to customize the bindings globally for all schemas. A schema can have only one such declaration, and the declaration affects all the imported and included schemas. The syntax for this declaration is shown below:

<globalBindings>
[ collectionType = "collectionType" ]
[ fixedAttributeAsConstantProperty= "true" | "false" | "1" | "0"]
[ generateIsSetMethod= "true" | "false" | "1" | "0" ]
[ enableFailFastCheck = "true" | "false" | "1" | "0" ]
[ choiceContentProperty = "true" | "false" | "1" | "0" ]
[ underscoreBinding = "asWordSeparator" | "asCharInWord" ]
[ typesafeEnumBase = "typesafeEnumBase" ]
[ typesafeEnumMemberName = "generateName" | "generateError" ]
[ enableJavaNamingConventions = "true" | "false" | "1" | "0" ]
[ modelGroupAsClass = "true" | "false" | "1" | "0" ]
[ <javaType> . . . </javaType> ]*
</globalBindings>


Previously we looked at how some elements, such as the Catalog, mapped to a java.util.List. A globally scoped globalBinding declaration can be used to specify the exact implementation the generated code may use:

<xsd:annotation>
 <xsd:appinfo>
 <jaxb:globalBindings collectionType="java.util.Vector"/>
 </xsd:appinfo>
</xsd:annotation>


The schemaBindings Declaration

The schemaBindings declaration has schema scope and is used to customize the bindings for the specific schema. The syntax for this declaration is shown below:

<schemaBindings>
 <package [ name = "packageName" ]
 [ <javadoc> . . . </javadoc> ]
 </package>
 <nameXmlTransform>
 [ <typeName [ suffix="suffix" ]
 [ prefix="prefix" ] /> ]
 [ <elementName [ suffix="suffix" ]
 [ prefix="prefix" ] />]
 [ <modelGroupName [ suffix="suffix" ]
 [ prefix="prefix" ] />]
 [ <anonymousTypeName [ suffix="suffix" ]
 [ prefix="prefix" ] />]
 </nameXmlTransform>
</schemaBindings>


The nameXmlTransform element in this declaration is specifically intended for use with UDDI 2.0, which contains many declarations that may cause duplicate names (or collisions) during generation of the Java bindings. For example, the following element in a UDDI schema will cause a fatal error:

<element type="uddi:bindingTemplate"/>


The nameXmlTransform element can be used to apply a suffix and prefix to such elements and avoid collisions. In the original purchaseorder.xsd example, the Java code was generated by default in the com.flutebank.schema package corresponding to the target namespace http://www.flutebank.com/schema of the schema. This can be changed to com.flutebank.custompackage by using a schema scoped schemaBinding declaration:

<xsd:annotation>
 <xsd:appinfo>
 <jaxb:globalBindings collectionType="java.util.Vector"/>
 <jaxb:schemaBindings>
 <jaxb:package name="com.flutebank.custompackage"/>
 </jaxb:schemaBindings>
 </xsd:appinfo>
</xsd:annotation>
<!--The rest of the original schema from Listing 13.1-->



The javadoc Declaration

This general-purpose declaration can be used to insert text that will appear as Javadoc comments in the generated Java code. It can be used only for component scope declaration. The syntax is:

<javadoc> Contents in Javadoc format </javadoc>


The sample usage of this declaration is shown below, along with the class declaration.

The class Declaration

This declaration can be used to specify the name of the Java interface or the implementation class to use for a specific element. This will be useful for tool vendors and providers in specifying their own implementation classes rather than the default JAXB implementation code.

<class [ name = "className"]>
 [ implClass= "implClass" ]
 [ <javadoc> . . . </javadoc> ]
</class>


In the original purchaseorder.xsd example, the billingaddress element was mapped to a Java interface named Billingaddress by default. This can be customized to the name PrimaryBillingAddress:

<xsd:element >
 <xsd:complexType>
 <xsd:annotation>
 <xsd:appinfo>
 <jaxb:class >
 <jaxb:javadoc>
 The custom Java interface corresponding to the billingaddress element
 in the schema
 </jaxb:javadoc>
 </jaxb:class>
 </xsd:appinfo>
 </xsd:annotation>
<!--The rest of the orignal schema from Listing 13.1-->



Listing 13.6 shows the effect of these annotations on the generated code. The interface is named PrimaryBillingAddress, it is now in the package com.flutebank.custompackage, and the specified Javadoc comments precede the standard comments.

Listing 13.6: Bindings affected by class and javadoc declarations
package com.flutebank.custompackage;
/**
 *
 *The custom Java interface corresponding to the billingaddress element in the schema
 * Java content class for anonymous complex type.
 * <p>The following schema fragment specifies the expected content contained within this
 * java content object.
 * <p>
 * <pre>
 * &lt;complexType>
 * &lt;complexContent>
 * &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 * &lt;sequence>
 * &lt;element ref="{http://www.flutebank.com/schema}name"/>
 * &lt;element ref="{http://www.flutebank.com/schema}street"/>
 * &lt;element ref="{http://www.flutebank.com/schema}city"/>
 * &lt;element ref="{http://www.flutebank.com/schema}state"/>
 * &lt;element ref="{http://www.flutebank.com/schema}zip"/>
 * &lt;/sequence>
 * &lt;/restriction>
 * &lt;/complexContent>
 * &lt;/complexType>
 * </pre>
 *
 */
public interface PrimaryBillingAddress {
 com.flutebank.custompackage.Name getName();
 void setName(com.flutebank.custompackage.Name value);
 com.flutebank.custompackage.Zip getZip();
 void setZip(com.flutebank.custompackage.Zip value);
 com.flutebank.custompackage.State getState();
 void setState(com.flutebank.custompackage.State value);
 com.flutebank.custompackage.City getCity();
 void setCity(com.flutebank.custompackage.City value);
 com.flutebank.custompackage.Street getStreet();
 void setStreet(com.flutebank.custompackage.Street value);
}



Java End example

The property Declaration

This declaration is used to customize the binding of an XML schema element to the Java representation as a property. The syntax for the property declaration is shown below:

<property
[ name = "propertyName"]
[ baseType = "propertyBaseType"]
[ collectionType = "propertyCollectionType" ]
[ fixedAttributeAsConstantProperty= "true" | "false" | "1" | "0"]
[ generateIsSetMethod= "true" | "false" | "1" | "0" ]
[ enableFailFastCheck="true" | "false" | "1" | "0" ]
[ choiceContentProperty = "true" | "false" | "1" | "0" ]
[ <javadoc> . . . </javadoc> ]
</property>


The generateIsSetMethod customization is important because it causes two additional property methods, isSetXXX() and unsetXXX(), to be included in the generated code (where XXX is the property name). The app code can use these methods to distinguish between schema default values and values occurring explicitly within an instance document. For example, the city and date elements are annotated in the original schema as shown below. This causes the name of the property in the Java code to be changed from city to billingcity and date to dateorderplaced. Had the original property been named Date, it might have caused a collision with the class java.util.Date during code generation, requiring a custom declaration.

<xsd:element type="xsd:string">
 <xsd:annotation>
 <xsd:appinfo>
 <jaxb:property generateIsSetMethod="true"/>
 </xsd:appinfo>
 </xsd:annotation>
 </xsd:element>
<xsd:element type="xsd:string">
 <xsd:annotation>
 <xsd:appinfo>
 <jaxb:property />
 </xsd:appinfo>
 </xsd:annotation>
 </xsd:element>


The effect of these declarations can be seen in the code generated for the interface PrimaryBillingAddress, which now contains the following methods:

 package com.flutebank.custompackage;
 public interface PrimaryBillingAddress {
// Other Java code here not shown
 com.flutebank.custompackage.City getBillingcity();
 void setBillingcity(com.flutebank.custompackage.City value);
 boolean isSetBillingcity();
 void unsetBillingcity();
 }


The javaType Declaration

This declaration is used to customize the bindings of an XML schema data type to the corresponding Java data type. The Java data type can be a Java built-in data type or a custom-defined type (e.g., an app-defined class). This declaration can be applied as an annotation to a specific element or included in the globalBinding declaration. The syntax for the javaType declaration is shown below:

<javaType 
[ xmlType="xmlType" ]
[ parseMethod="parseMethod" ]
[ printMethod="printMethod" ]>


In the original purchaseorder.xsd, the identifier element was defined as a string and therefore automatically mapped to a Java interface called Identifier, with getValue and setValue methods that treated the underlying property as a String. If it is known that this value will always be an integer, the default mapping can be overridden with the javaType declaration, as shown below:

<xsd:element type="xsd:string">
 <xsd:annotation>
 <xsd:appinfo>
 <jaxb:javaType />
 </xsd:appinfo>
 </xsd:annotation>
 </xsd:element>


The Java interface generated as a result of this annotation is:

package com.flutebank.custompackage;
 public interface Identifier extends javax.xml.bind.Element{
 int getValue();
 void setValue(int value);
}


The typesafeEnum Declaration

Earlier, we mentioned how an enumeration of base type "xs:NCName" maps to typesafe class. This binding declaration can be used to customize the generated bindings for simple type definitions with enumeration facets. The syntax is shown below:

<typesafeEnumClass name = "enumClassName">
 <typesafeEnumMember name = "enumMemberName">
 [ value = "enumMemberValue" ]
 [ <javadoc> enumMemberJavadoc </javadoc> ]
 </typesafeEnumMember>
 [ <javadoc> enumClassJavadoc </javadoc> ]
</typesafeEnumClass>


Using typesafeEnums enables schema enumeration values to be mapped to Java constants. This optimizes performance by making it possible for constants to be compared, instead of string comparisons. To understand the difference, let us look at the sample schema discussed earlier in the "Enumerations" section:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 elementFormDefault="qualified" >
 <xsd:element >
 <xsd:simpleType>
 <xsd:restriction base="xsd:string">
 <xsd:enumeration value="Saturday"/>
 <xsd:enumeration value="Sunday"/>
 <xsd:enumeration value="Monday"/>
 <xsd:enumeration value="Tuesday"/>
 <xsd:enumeration value="Wednesday"/>
 <xsd:enumeration value="Thursday"/>
 <xsd:enumeration value="Friday"/>
 </xsd:restriction>
 </xsd:simpleType>
 </xsd:element>
</xsd:schema>


When compiled, this generates the Day interface, corresponding to the enumeration:

public interface Day extends javax.xml.bind.Element{
 String getValue();
 void setValue(String value);
}


This same schema can be customized with the typesafeEnum declaration, as shown below:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 elementFormDefault="qualified"
 xmlns:jaxb="http://java.oracle.com/xml/ns/jaxb" jaxb:version="1.0">
 <xsd:element >
 <xsd:simpleType>
 <xsd:annotation>
 <xsd:appinfo>
 <jaxb:typesafeEnumClass />
 </xsd:appinfo>
 </xsd:annotation>
 <xsd:restriction base="xsd:string">
 <xsd:enumeration value="Saturday"/>
 <xsd:enumeration value="Sunday"/>
 <xsd:enumeration value="Monday"/>
 <xsd:enumeration value="Tuesday"/>
 <xsd:enumeration value="Wednesday"/>
 <xsd:enumeration value="Thursday"/>
 <xsd:enumeration value="Friday"/>
 </xsd:restriction>
 </xsd:simpleType>
 </xsd:element>
</xsd:schema>


When compiled, the Day interface now uses a type-safe class instead of strings:

public interface Day extends javax.xml.bind.Element{
 generated.WeekDays getValue();
 void setValue(generated.WeekDays value);
}


Listing 13.7 also shows the generated class Weekdays. Notice the private constructor and the parse method.

Listing 13.7: A typesafe-enumeration-generated binding
public class WeekDays {
 public final static WeekDays SATURDAY = new WeekDays("Saturday");
 private final static Object $$$_SATURDAY = "Saturday";
 public final static WeekDays SUNDAY = new WeekDays("Sunday");
 private final static Object $$$_SUNDAY = "Sunday";
 public final static WeekDays MONDAY = new WeekDays("Monday");
 private final static Object $$$_MONDAY = "Monday";
 public final static WeekDays TUESDAY = new WeekDays("Tuesday");
 private final static Object $$$_TUESDAY = "Tuesday";
 public final static WeekDays WEDNESDAY = new WeekDays("Wednesday");
 private final static Object $$$_WEDNESDAY = "Wednesday";
 public final static WeekDays THURSDAY = new WeekDays("Thursday");
 private final static Object $$$_THURSDAY = "Thursday";
 public final static WeekDays FRIDAY = new WeekDays("Friday");
 private final static Object $$$_FRIDAY = "Friday";
 private final String value;
 private WeekDays(String v) {
 value = v;
 }
 public String toString() {
 return value;
 }
 public final int hashCode() {
 return super.hashCode();
 }
 public final boolean equals(Object o) {
 return super.equals(o);
 }
 public static WeekDays parse(String str) {
 if ($$$_SATURDAY.equals(str)) {
 return SATURDAY;
 }
 if ($$$_SUNDAY.equals(str)) {
 return SUNDAY;
 }
 if ($$$_MONDAY.equals(str)) {
 return MONDAY;
 }
 if ($$$_TUESDAY.equals(str)) {
 return TUESDAY;
 }
 if ($$$_WEDNESDAY.equals(str)) {
 return WEDNESDAY;
 }
 if ($$$_THURSDAY.equals(str)) {
 return THURSDAY;
 }
 if ($$$_FRIDAY.equals(str)) {
 return FRIDAY;
 }
 return null;
 }
}



Java End example

JaVa
   
Comments