December 5, 2011

Reusing Generated JAXB Classes

In this post I will demonstrate how to leverage the -episode XJC extension to reuse classes previously generated from.an XML schema.  This is useful when an XML schema is imported by other XML schemas and you do not want the same classes generated each time.

Imported Schema (Product.xsd)

The following XML schema represents basic information about a product.  Product is a common concept in this example domain so I have decided to define one representation that can be leveraged by other schemas, rather than having each schema define its own representation of product information.

<?xml version="1.0" encoding="UTF-8"?>
<schema 
    xmlns="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="http://www.example.org/Product" 
    xmlns:tns="http://www.example.org/Product" 
    elementFormDefault="qualified">
    <element name="product">
        <complexType>
            <sequence>
                <element name="id" type="string"/>
                <element name="name" type="string"/>
            </sequence>
        </complexType>
    </element>
</schema>

Since multiple XML schemas import Product.xsd we can leverage episode files so that the classes corresponding to Product.xsd are only generated once.  The following XJC call demonstrates how to generate an episode file called product.episode along with the generated classes:

xjc -d out -episode product.episode Product.xsd

Importing Schema (ProductPurchaseRequest.xsd)

Below is an example of an XML schema that imports Product.xsd:

<?xml version="1.0" encoding="UTF-8"?>
<schema 
    xmlns="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="http://www.example.org/ProductPurchaseRequest" 
    xmlns:tns="http://www.example.org/ProductPurchaseRequest"
    xmlns:prod="http://www.example.org/Product" 
    elementFormDefault="qualified">
    <import namespace="http://www.example.org/Product" schemaLocation="Product.xsd"/>
    <element name="purchase-request">
        <complexType>
            <sequence>
                <element ref="prod:product" maxOccurs="unbounded"/>
            </sequence>
        </complexType>
    </element>
</schema>

When we generate classes from this XML schema we will reference the episode file we created when we generated Java classes from Product.xsd.  If we do not specify the episode file then classes will be generated for both ProductPurchaseRequest.xsd and Product.xsd:

xjc -d out ProductPurchaseRequest.xsd -extension -b product.episode

Another Importing Schema (ProductQuoteRequest.xsd)

Below is another example of an XML schema that imports Product.xsd:

<?xml version="1.0" encoding="UTF-8"?>
<schema 
    xmlns="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="http://www.example.org/ProductQuoteRequest" 
    xmlns:tns="http://www.example.org/ProductQuoteRequest" 
    xmlns:prod="http://www.example.org/Product" 
    elementFormDefault="qualified">
    <import namespace="http://www.example.org/Product" schemaLocation="Product.xsd"/>
    <element name="quote">
        <complexType>
            <sequence>
                <element ref="prod:product"/>
            </sequence>
        </complexType>
    </element>
</schema>

Again when we generate classes from this XML schema we will reference the episode file we created when we generated Java classes from Product.xsd.

xjc -d out ProductQuoteRequest.xsd -extension -b product.episode

How Does it Work? (product.episode)

For those of you curious how this works.  The episode file generated by XJC is really just a standard JAXB bindings file that is used to customize the class generation.  This generated bindings/episode file contains entries that tells XJC that a class already exists for this type.  You could write this file by hand, but -episode flag to XJC does it for you.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bindings version="2.1" xmlns="http://java.sun.com/xml/ns/jaxb">
  <!--

This file was generated by the JavaTM Architecture for XML Binding
(JAXB) Reference Implementation, vJAXB 2.1.10 in JDK 6 See 
<a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
Any modifications to this file will be lost upon recompilation of the 
source schema. 
Generated on: 2011.11.02 at 03:40:10 PM EDT 

  -->
  <bindings scd="x-schema::tns" 
    xmlns:tns="http://www.example.org/Product">
    <schemaBindings map="false"/>
    <bindings scd="tns:product">
      <class ref="org.example.product.Product"/>
    </bindings>
  </bindings>
</bindings>


Further Reading

If you enjoyed this post, then you may also be interested in:

5 comments:

  1. Hi
    What if the Product.xsd file had customizations like inheritance:implements plugin from xjc-basics plugin.I don think then we can import it
    -Su

    ReplyDelete
  2. Hi Su,

    Other schemas can still import Product.xsd in your use case. This is one of the use cases where episode files can be leveraged since it allows you to target XJC extensions at a particular shcmea, and have these generated classes available to be referenced by other generated classes.

    -Blaise

    ReplyDelete
  3. One thing to note is that episode files and the JAX-WS-RI wsimport and CXF's wsdl2java commands are relatively incompatible. See the bug logged at:

    http://java.net/jira/browse/JAXB-514

    Thus, if you'd like to re-use the JAXB code for multiple JAX-WS based WebServices, the episode idea doesn't really work. :-(

    ReplyDelete
  4. Help! (I realize I'm coming two years late to the party. I hope you're still listening.) Anyway, thanks for this article, it is exactly what I was trying to do. However, when I bring in the episode file, my second xjc task doesn't output the java files. I'm using com.sun.tools.xjc.XJCTask inside ant rather than calling xjc from a command line, but I don't think that should matter. (Right?)

    I have the following in my build.xml. If I remove binding="common.episode" from the second task, then it works (expect that it puts all the classes for the import of common.xsd there, as well.)






















    ReplyDelete
    Replies
    1. You XML content didn't survive the limitations of the content area. Are you able to post this as a question on Stack Overflow?

      Delete