JAXB XJC code generation - adding @XmlRootElement and Joda DateTime

  • Here's an interesting scenario that I encountered the other day. I did eventually reach a solution on my own. However, I'd welcome any comments and better approaches.



    The requirements



    I want to generate JAXB objects based on a collection of XSDs using XJC as part of a Maven build. I'll be using JAXB-RI 2.1 as the implementation.



    In addition, I want to make sure that all objects implement a signature interface (e.g. MySignature) which has no methods. Also, I want to avoid using XmlGregorianCalendar and have Joda DataTime instead (with a suitable adapter that I'll provide called DateUtils with parse() and format() methods).



    Finally, I want to be able to select certain objects to act as root elements so I'll need to selectively add @XmlRootElement to some objects, and I have suitable XPath expressions to locate them.



    I can't make any changes to the XSDs.



    The approach



    Step 1 - Configuring the pom.xml



    Configure Maven to use the XJC plugin as follows:





    <build>
    <plugins>
    <plugin>
    <groupId>org.jvnet.jaxb2.maven2</groupId>
    <artifactId>maven-jaxb2-plugin</artifactId>
    <version>0.7.4</version>
    <executions>
    <execution>
    <id>generate-domain1</id>
    <goals>
    <goal>generate</goal>
    </goals>
    <configuration>
    <strict>false</strict>
    <schemaIncludes>
    <value>domain1.xsd</value>
    </schemaIncludes>
    <bindingIncludes>
    <include>domain1-bindings.xjb</include>
    </bindingIncludes>
    <extension>true</extension>
    <generatePackage>org.example.domain1</generatePackage>
    <generateDirectory>${project.build.directory}/generated-sources/domain1</generateDirectory>
    <args>
    <arg>-Xannotate</arg>
    </args>
    <plugins>
    <plugin>
    <groupId>org.jvnet.jaxb2_commons</groupId>
    <artifactId>jaxb2-basics</artifactId>
    <version>0.6.0</version>
    </plugin>
    <plugin>
    <groupId>org.jvnet.jaxb2_commons</groupId>
    <artifactId>jaxb2-basics-annotate</artifactId>
    <version>0.6.0</version>
    </plugin>
    </plugins>
    </configuration>
    </execution>
    </executions>
    </plugin>
    </plugins>
    </build>


    The use of a dedicated execution configuration is there to allow additional mutually exclusive XSDs to be built using a different target output path.



    Step 2 - Configure the bindings



    Include the following as src/main/resources/domain1-bindings.xjb.



    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <jaxb:bindings
    xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:annox="http://annox.dev.java.net"
    xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd"
    jaxb:extensionBindingPrefixes="xjc annox"
    version="2.1">

    <jaxb:globalBindings>
    <jaxb:serializable uid="1"/>
    <!-- All generated classes must have MySignature interface (supplied in dependencies) -->
    <xjc:superInterface name="org.example.MySignature"/>
    <!-- All temporal fields are implemented as Joda DateTime and use DateUtils as an adapter -->
    <jaxb:javaType
    name="org.joda.time.DateTime"
    xmlType="xs:time"
    parseMethod="org.example.DateUtils.parseDateTime"
    printMethod="org.exmaple.DateUtils.formatDateTime"
    />
    </jaxb:globalBindings>

    <!-- Application of annotations to selected classes within schemas -->

    <!-- org.example.SomeRootType @XmlRootElement -->
    <jaxb:bindings schemaLocation="domain1.xsd" node="/xs:schema">
    <jaxb:bindings node="xs:complexType[@name='SomeRootType']">
    <annox:annotate>
    <annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement" name="SomeRootType"/>
    </annox:annotate>
    </jaxb:bindings>
    </jaxb:bindings>

    </jaxb:bindings>


    Troubleshooting



    If you encounter the infamous




    Unsupported binding namespace "". Perhaps you meant "http://annox.dev.java.net"




    then you need to examine your bindings.xjb to make sure that you're using the correct namespace URIs (as defined above) and have included them into extensionBindingPrefixes.


  • The <jaxb:javaType /> tag generates a new org.w3._2001.xmlschema.AdapterN class for each new javaType definition. So a better approach would be:




    • Create a class extending XmlAdapter<String, _To_>. In it you will have to override the both methods defined. So in example, the class could be org.example.DateUtilsAdapter.

    • Use this tag instead:



      <xjc:javaType
      name="org.joda.time.DateTime"
      xmlType="xs:time"
      adapter="org.example.DateUtilsAdapter" />



    Source


    I have an example with a java.util.Date Adapter (JaxbDateAdapter). Source here

  • I tried your example in Eclipse and got a warning:



    cvc-complex-type.3.2.2: Attribute 'jaxb:extensionBindingPrefixes' is not allowed to appear in 
    element 'jaxb:bindings'.


    When I changed the URL from 2.0 to 2.1 it removed the warning.



    Before: http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd

    After: http://java.sun.com/xml/ns/jaxb/bindingschema_2_1.xsd



    <jaxb:bindings
    xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:annox="http://annox.dev.java.net"
    xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_1.xsd"
    jaxb:extensionBindingPrefixes="xjc annox"
    version="2.1">

License under CC-BY-SA with attribution


Content dated before 7/24/2021 11:53 AM