Thursday, 11 April 2013

Practice of customize JAXB Schema-2-JAVA use xjc

JAXB Marshalling with Custom Namespace Prefixes
  • You have an XML schema with multiple XML namespaces.
  • You generate a JAXB model with xjc.
  • You build a JAXB document model and use the JAXB Marshaller to create XML from the model.
  • You want to override the default namespace prefixes ns1, ns2, ... created by the Marshaller.
XJC bind Schema-2-java
  • You want use customization java package
  • You want use more meanful java property name to replace xml element/attribute, default use element/attribute name
  • You want to override the default XmlAdapter Adapter1.java, Adapter2.java... created by xjc.

Target xsd file
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:ns1="http://seanshou.blogspot.com/XMLSchema" 
targetNamespace="http://seanshou.blogspot.com/XMLSchema" 
elementFormDefault="qualified" 
attributeFormDefault="unqualified">
 <xs:element name="comment" type="xs:string"/>
 <xs:element name="employee" type="ns1:EmployeeType">
  <xs:annotation>
   <xs:documentation>Comment describing your root element</xs:documentation>
  </xs:annotation>
 </xs:element>
 <xs:complexType name="USAddress">
  <xs:sequence>
   <xs:element name="name" type="xs:string"/>
   <xs:element name="street" type="xs:string"/>
   <xs:element name="city" type="xs:string"/>
   <xs:element name="state" type="xs:string"/>
   <xs:element name="zip" type="xs:string"/>
  </xs:sequence>
  <xs:attribute name="country" type="xs:NMTOKEN" use="required" fixed="US"/>
 </xs:complexType>
 <xs:complexType name="EmployeeType">
  <xs:sequence>
   <xs:element name="address" type="ns1:USAddress"/>
   <xs:element name="paySlip" type="ns1:payment"/>
   <xs:element ref="ns1:comment"/>
  </xs:sequence>
  <xs:attribute name="userId" type="xs:int" use="required"/>
 </xs:complexType>
 <xs:complexType name="payment">
  <xs:sequence>
   <xs:element name="baseSalary" type="xs:int"/>
   <xs:element name="supernation" type="xs:integer" nillable="true" minOccurs="0"/>
   <xs:element name="bonus" type="xs:integer" nillable="true" minOccurs="0"/>
  </xs:sequence>
 </xs:complexType>
</xs:schema>
  • line[5]: The generated classes by xjc will qualified by namespace: http://seanshou.blogspot.com/XMLSchema
  • line[33,34]: The customization to use use-defined adapter BigIntegerXmlAdapter for xs:integer
Customized external bindings file
<?xml version="1.0" encoding="UTF-8"?>
<jxb:bindings
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
        xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
        xmlns:namespace="http://jaxb2-commons.dev.java.net/namespace-prefix"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd
        http://jaxb2-commons.dev.java.net/namespace-prefix http://java.net/projects/jaxb2-commons/sources/svn/content/namespace-prefix/trunk/src/main/resources/prefix-namespace-schema.xsd"
        jxb:extensionBindingPrefixes="xjc"
        version="2.1">
    <jxb:bindings schemaLocation="Employee-Definiation.xsd" node="/xs:schema">
        <jxb:globalBindings fixedAttributeAsConstantProperty="false" collectionType="java.util.ArrayList"
                            typesafeEnumBase="xs:NCName" choiceContentProperty="false"
                            typesafeEnumMemberName="generateError"
                            enableFailFastCheck="false" generateIsSetMethod="false" underscoreBinding="asCharInWord">
            <xjc:javaType name="java.math.BigInteger" xmlType="xs:integer"
                          adapter="com.blogspot.seanshou.jaxb.adapter.BigIntegerXmlAdapter"/>
        </jxb:globalBindings>
        <jxb:schemaBindings>
            <jxb:package name="com.blogspot.seanshou.jaxb.xml"/>
        </jxb:schemaBindings>
        <jxb:bindings>
            <namespace:prefix name="NS1"/>
        </jxb:bindings>
        <jxb:bindings node="//xs:element[@name='employee']">
            <jxb:class name="EmployeePojo"/>
        </jxb:bindings>
        <jxb:bindings node="//xs:complexType[@name='EmployeeType']">
            <jxb:bindings node=".//xs:element[@name='address']">
              <jxb:property name="mailAddress"/>
            </jxb:bindings>
        </jxb:bindings>
    </jxb:bindings>
</jxb:bindings>
  • line[17,18]: customize to special java type use pre-defined 'XmlAdater'
  • line[21]: customize to java package name
  • Note that this customization is per namespace. That is, even if your schema is split into multiple schema documents, you cannot put them into different packages if they are all in the same namespace
  • line[24]: customize namespace prefix when XJC convert Schema-2-Java, the prefix 'NS1' will be append package-info.java
  • use JAXB Namespace-prefix plugin
  • line[26,28]: binding root xml elemment employee to java class EmployeeType
  • line[30,32]: binding child xml elemment address of employee to java property mailAddress of class EmployeeType

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.blogspot.seanshou.jaxb</groupId>
    <artifactId>JAXB-Schema-to-Java-showcase</artifactId>
    <version>1.0</version>
    <build>
        <plugins>
            <plugin>
                <!-- jaxb plugin -->
                <groupId>org.jvnet.jaxb2.maven2</groupId>
                <artifactId>maven-jaxb2-plugin</artifactId>
                <version>0.8.3</version>
                <configuration>
                    <!--
                    <generatePackage>com.blogspot.seanshou.jaxb.xml</generatePackage>
                    <catalog>src/main/resources/catalog.xml</catalog>
                    -->
                    <schemaDirectory>src/main/resources</schemaDirectory>
                    <schemaIncludes>
                        <include>*.xsd</include>
                    </schemaIncludes>
                    <bindingDirectory>src/main/resources</bindingDirectory>
                    <bindingIncludes>
                        <include>bindings.xml</include>
                    </bindingIncludes>
                    <args>
                        <arg>-extension</arg>
                        <arg>-Xnamespace-prefix</arg>
                    </args>
                    <removeOldOutput>true</removeOldOutput>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                    </execution>
                </executions>
                    <dependencies>
                        <dependency>
                            <groupId>org.jvnet.jaxb2_commons</groupId>
                            <artifactId>jaxb2-namespace-prefix</artifactId>
                            <version>1.1</version>
                        </dependency>
                    </dependencies>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.2.6</version>
        </dependency>
        <dependency>
            <groupId>javax.xml</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.1</version>
        </dependency>
    </dependencies>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
</project>

Pre-defined BigIntegerXmlApater.java
package com.blogspot.seanshou.jaxb.adapter;

import javax.xml.bind.DatatypeConverter;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import java.math.BigInteger;

import static org.apache.commons.lang3.StringUtils.EMPTY;
import static org.apache.commons.lang3.StringUtils.isBlank;


public class BigIntegerXmlAdapter extends XmlAdapter<String, BigInteger> {
    @Override
    public BigInteger unmarshal(String value) throws Exception {
        if (isBlank(value)) {
            return BigInteger.ZERO;
        }
        return DatatypeConverter.parseInteger(value);
    }

    @Override
    public String marshal(BigInteger value) throws Exception {
        if (value == null) {
            return EMPTY;
        }
        return DatatypeConverter.printInteger(value);
    }
}


Generated classes by xjc
  • EmployeePojo.java
  • package com.blogspot.seanshou.jaxb.xml;
    
    import javax.xml.bind.JAXBElement;
    import javax.xml.namespace.QName;
    
    public class EmployeePojo extends JAXBElement<EmployeeType>
    {
    
        protected final static QName NAME = new QName("http://seanshou.blogspot.com/XMLSchema", "employee");
    
        public EmployeePojo(EmployeeType value) {
            super(NAME, ((Class) EmployeeType.class), null, value);
        }
    
        public EmployeePojo() {
            super(NAME, ((Class) EmployeeType.class), null, null);
        }
    
    }
    
    • line[6]: Customized by line[26-28] in bindings.xml
  • EmployeeType.java
  • package com.blogspot.seanshou.jaxb.xml;
    
    import javax.xml.bind.annotation.XmlAccessType;
    import javax.xml.bind.annotation.XmlAccessorType;
    import javax.xml.bind.annotation.XmlAttribute;
    import javax.xml.bind.annotation.XmlElement;
    import javax.xml.bind.annotation.XmlType;
    
    
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "EmployeeType", propOrder = {
        "mailAddress",
        "paySlip",
        "comment"
    })
    public class EmployeeType {
    
        @XmlElement(name = "address", required = true)
        protected USAddress mailAddress;
        @XmlElement(required = true)
        protected Payment paySlip;
        @XmlElement(required = true)
        protected String comment;
        @XmlAttribute(name = "userId", required = true)
        protected int userId;
    
        public USAddress getMailAddress() {
            return mailAddress;
        }
    
        public void setMailAddress(USAddress value) {
            this.mailAddress = value;
        }
    
        public Payment getPaySlip() {
            return paySlip;
        }
    
        public void setPaySlip(Payment value) {
            this.paySlip = value;
        }
    
        public String getComment() {
            return comment;
        }
    
    
        public void setComment(String value) {
            this.comment = value;
        }
    
    
        public int getUserId() {
            return userId;
        }
    
        public void setUserId(int value) {
            this.userId = value;
        }
    
    }
    
    • line[18,19]: Customized by line[30-32] in bindings.xml
  • Payment.java
  • package com.blogspot.seanshou.jaxb.xml;
    
    import java.math.BigInteger;
    import javax.xml.bind.JAXBElement;
    import javax.xml.bind.annotation.XmlAccessType;
    import javax.xml.bind.annotation.XmlAccessorType;
    import javax.xml.bind.annotation.XmlElementRef;
    import javax.xml.bind.annotation.XmlType;
    
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "payment", propOrder = {
        "baseSalary",
        "supernation",
        "bonus"
    })
    public class Payment {
    
        protected int baseSalary;
        @XmlElementRef(name = "supernation", namespace = "http://seanshou.blogspot.com/XMLSchema", type = JAXBElement.class)
        protected JAXBElement<BigInteger> supernation;
        @XmlElementRef(name = "bonus", namespace = "http://seanshou.blogspot.com/XMLSchema", type = JAXBElement.class)
        protected JAXBElement<BigInteger> bonus;
    
        public int getBaseSalary() {
            return baseSalary;
        }
    
        public void setBaseSalary(int value) {
            this.baseSalary = value;
        }
    
        public JAXBElement<BigInteger> getSupernation() {
            return supernation;
        }
    
        public void setSupernation(JAXBElement<BigInteger> value) {
            this.supernation = value;
        }
    
        public JAXBElement<BigInteger> getBonus() {
            return bonus;
        }
    
        public void setBonus(JAXBElement<BigInteger> value) {
            this.bonus = value;
        }
    
    }
    
  • USAddress.java
  • package com.blogspot.seanshou.jaxb.xml;
    
    import javax.xml.bind.annotation.XmlAccessType;
    import javax.xml.bind.annotation.XmlAccessorType;
    import javax.xml.bind.annotation.XmlAttribute;
    import javax.xml.bind.annotation.XmlElement;
    import javax.xml.bind.annotation.XmlSchemaType;
    import javax.xml.bind.annotation.XmlType;
    import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
    import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
    
    
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "USAddress", propOrder = {
        "name",
        "street",
        "city",
        "state",
        "zip"
    })
    public class USAddress {
    
        @XmlElement(required = true)
        protected String name;
        @XmlElement(required = true)
        protected String street;
        @XmlElement(required = true)
        protected String city;
        @XmlElement(required = true)
        protected String state;
        @XmlElement(required = true)
        protected String zip;
        @XmlAttribute(name = "country", required = true)
        @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
        @XmlSchemaType(name = "NMTOKEN")
        protected String country;
    
        public String getName() {
            return name;
        }
    
        public void setName(String value) {
            this.name = value;
        }
    
        public String getStreet() {
            return street;
        }
    
        public void setStreet(String value) {
            this.street = value;
        }
    
        public String getCity() {
            return city;
        }
    
        public void setCity(String value) {
            this.city = value;
        }
    
        public String getState() {
            return state;
        }
    
        public void setState(String value) {
            this.state = value;
        }
    
        public String getZip() {
            return zip;
        }
    
        public void setZip(String value) {
            this.zip = value;
        }
    
        public String getCountry() {
            if (country == null) {
                return "US";
            } else {
                return country;
            }
        }
    
        public void setCountry(String value) {
            this.country = value;
        }
    }
    
  • ObjectFactory.java
  • package com.blogspot.seanshou.jaxb.xml;
    
    import java.math.BigInteger;
    import javax.xml.bind.JAXBElement;
    import javax.xml.bind.annotation.XmlElementDecl;
    import javax.xml.bind.annotation.XmlRegistry;
    import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
    import javax.xml.namespace.QName;
    import com.blogspot.seanshou.jaxb.adapter.BigIntegerXmlAdapter;
    
    @XmlRegistry
    public class ObjectFactory {
    
        private final static QName _Comment_QNAME = new QName("http://seanshou.blogspot.com/XMLSchema", "comment");
        private final static QName _PaymentSupernation_QNAME = new QName("http://seanshou.blogspot.com/XMLSchema", "supernation");
        private final static QName _PaymentBonus_QNAME = new QName("http://seanshou.blogspot.com/XMLSchema", "bonus");
    
        public ObjectFactory() {
        }
    
        public EmployeeType createEmployeeType() {
            return new EmployeeType();
        }
    
        public Payment createPayment() {
            return new Payment();
        }
    
        public USAddress createUSAddress() {
            return new USAddress();
        }
    
        @XmlElementDecl(namespace = "http://seanshou.blogspot.com/XMLSchema", name = "comment")
        public JAXBElement<String> createComment(String value) {
            return new JAXBElement<String>(_Comment_QNAME, String.class, null, value);
        }
    
        @XmlElementDecl(namespace = "http://seanshou.blogspot.com/XMLSchema", name = "employee")
        public EmployeePojo createEmployeePojo(EmployeeType value) {
            return new EmployeePojo(value);
        }
    
        @XmlElementDecl(namespace = "http://seanshou.blogspot.com/XMLSchema", name = "supernation", scope = Payment.class)
        @XmlJavaTypeAdapter(BigIntegerXmlAdapter.class)
        public JAXBElement<BigInteger> createPaymentSupernation(BigInteger value) {
            return new JAXBElement<BigInteger>(_PaymentSupernation_QNAME, BigInteger.class, Payment.class, value);
        }
    
        @XmlElementDecl(namespace = "http://seanshou.blogspot.com/XMLSchema", name = "bonus", scope = Payment.class)
        @XmlJavaTypeAdapter(BigIntegerXmlAdapter.class)
        public JAXBElement<BigInteger> createPaymentBonus(BigInteger value) {
            return new JAXBElement<BigInteger>(_PaymentBonus_QNAME, BigInteger.class, Payment.class, value);
        }
    }
    
    • line[44,50]: Customized by line[17-18] in bindings.xml
  • package-info.java
  • @javax.xml.bind.annotation.XmlSchema(namespace = "http://seanshou.blogspot.com/XMLSchema", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED, xmlns = {
        @javax.xml.bind.annotation.XmlNs(namespaceURI = "http://seanshou.blogspot.com/XMLSchema", prefix = "NS1")
    })
    package com.blogspot.seanshou.jaxb.xml;
    
    • line[2]: Customized by line[24] in bindings.xml
Reference

No comments:

Post a Comment