Generating a Jaxb Class That Implements an Interface

Generating a JAXB class that implements an interface

Unfortunately, it looks like the interface-injection plugin mentioned in some of the other answers is no longer well-supported. In fact, I'm having trouble finding the JAR for download.

Thankfully, the JAXB2 Basics Plugins provides a similar mechanism for adding an interface to the generated JAXB stubs (see the Inheritance plugin).

The Inheritance plugin documentation has an example showing what the XML schema file might look like. However, since you cannot modify the schema, you can use an external bindings file instead:

<?xml version="1.0"?>
<jxb:bindings version="1.0"
xmlns:jxb="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:inheritance="http://jaxb2-commons.dev.java.net/basic/inheritance"
jxb:extensionBindingPrefixes="xjc">

<jxb:bindings schemaLocation="xsd/adult.xsd">
<jxb:bindings node="//xs:complexType[@name='Person']">
<inheritance:implements>mypackage.Hello</inheritance:implements>
</jxb:bindings>
</jxb:bindings>

</jxb:bindings>

The JAXB2 Basics Plugins documentation includes instructions for using the plugin with Ant and Maven. You can also use it straight from the command line, but the command is a bit messy (due to the number of jars you have to add to the classpath):

java -jar jaxb-xjc.jar 
-classpath jaxb2-basics-0.5.3.jar,jaxb2-basics-runtime-0.5.3.jar,
jaxb2-basics-tools-0.5.3.jar,commons-beanutils-0.5.3.jar,
commons-lang.jar,commons-logging.jar
-p mypackage.myxml -extension -Xinheritance xsd/adult.xsd -b binding.xjb

The JAXB2 Basics Plugins provides a number of other utilities which you might also find useful (such as autogeneration of equals, hashCode, and toString methods).

JAXB - generated classes implement interface

The interface injection plugin for XJC lets you do this.

How to add interface on autogenerated jaxb elements by xsdtojava?

I had to add an /xs:complexType to the node. Don't know why that worked though.

Important to note the single slash before the complexType!

<jaxb:bindings schemaLocation="xsd/my.xsd">
<jaxb:bindings node="//xs:element[@name='thelistelement']/xs:complexType">
<inheritance:implements>MyInterface</inheritance:implements>
</jaxb:bindings>
</jaxb:bindings>

JAXB generated classes of certain types implementing a custom interface

Thanks to lexicore for the prompt and detailed answers. However that approach wasn't working for me, so I ended up with the following solution...

Because I am using Ant to invoke XJC, I ended up leveraging Ant's <copy filtering="true"...> capabilities to dynamically generate the binding file.

Here is my binding "template" file (bindings-common.xml):

<?xml version="1.0"?>
<jaxb:bindings
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:inheritance="http://jaxb2-commons.dev.java.net/basic/inheritance"
xmlns:my="http://http://example.com/core"
jaxb:extensionBindingPrefixes="inheritance"
version="2.1">
<jaxb:bindings>
<jaxb:globalBindings localScoping="toplevel">
<jaxb:serializable/>
<xjc:simple/>
</jaxb:globalBindings>
</jaxb:bindings>

<jaxb:bindings
schemaLocation="@bindingSchema@"
node="/xs:schema">

<jaxb:bindings node="//xs:complexType[@name='addressType']">
<inheritance:implements>com.example.validator.ValidatableAddress</inheritance:implements>
</jaxb:bindings>
</jaxb:bindings>

</jaxb:bindings>

Note this line:

<jaxb:bindings
schemaLocation="@bindingSchema@"
node="/xs:schema">

This variable will get populated by Ant for each of the schemas that I am processing:

<property name="jaxb.binding.template" value="../etc/form-schemas/bindings-common.xml"/>
<property name="jaxb.binding.file" value="${jaxb.src.dir}/bindings-common${schema.version}.xml"/>

<echo message="Filtering ${jaxb.binding.file} using template ${jaxb.binding.template}"/>

<copy file="${jaxb.binding.template}"
tofile="${jaxb.binding.file}"
filtering="true">

<filterset>
<filter token="bindingSchema" value="../../etc/form-schemas/${schema.version}/common.xsd"/>
</filterset>
</copy>

<xjc destdir="${jaxb.src.dir}"
extension="true"
schema="${schema.file}"
package="${package}"
binding="${jaxb.binding.file}">

<arg value="-episode"/>
<arg value="${jaxb.src.dir}/common${schema.version}.episode"/>
<arg line="-Xinheritance"/>

<!-- Plugins -->
<classpath>
<fileset dir="../build-libs/">
<!-- JAXB2 Basics library -->
<include name="jaxb2-basics-0.6.4.jar"/>
<!-- JAXB2 Basics library dependencies -->
<include name="jaxb2-basics-runtime-0.6.4.jar"/>
<include name="jaxb2-basics-tools-0.6.4.jar"/>
<include name="javaparser-1.0.8.jar"/>
<include name="commons-beanutils-*.jar"/>
<include name="commons-lang-*.jar"/>
<include name="commons-logging-*.jar"/>
</fileset>
</classpath>
</xjc>
</target>

Hopefully this will help future generations of JAXB victims.

How to generate a Java class which implements Serializable interface from xsd using JAXB?

Serializable

Use xjc:serializable in a custom bindings file to add the java.io.Serializable interface to your classes along with a serialVersionUID:

<?xml version="1.0" encoding="UTF-8"?>
<bindings xmlns="http://java.sun.com/xml/ns/jaxb"
xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xsi:schemaLocation="
http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd"
version="2.1">
<globalBindings>
<serializable uid="1" />
</globalBindings>
</bindings>

toString()

Use a superclass (see xjc:superClass) from which all your bound classes will inherit. This class won’t be generated by xjc so you are free to create it as you please (here with a toString() implementation):

<?xml version="1.0" encoding="UTF-8"?>
<bindings xmlns="http://java.sun.com/xml/ns/jaxb"
xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xsi:schemaLocation="
http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd"
version="2.1">
<globalBindings>
<serializable uid="1" />
<xjc:superClass name="the.package.to.my.XmlSuperClass" />
</globalBindings>
</bindings>

Two sets of JAXB models sharing the same set of interfaces

This is not quite trivial.

To the best of my knowledge, there is no real "guide" on XJC internals. I had to learn by doing. You might want to take a look at some of the existing XJC plugin to see how they accomplish things.

I'm also not quite sure why you'd nee to "remove existing fields and add them with other types". To me, this does not seem consistent with your task.

I'd decompose this task in three tasks:

  1. Generate immutable interfaces in a specified package.
  2. Automatically implement interfaces from a specified package.
  3. Generate immutable classes.

In the build I'd define 3 executions then:

  • Generate immutable interfaces (using 1).
  • Generate mutable classes (normal XJC + 2).
  • Generate immutable classes (3 + 2).

immutable-xjc probably already implements 3, so I'd write plugins for 1 and 2.

For 1 and 2, I think, I'd try to work on the CodeModel level. Just with the generated classes, not messing with the model and the outline.

In 1, you basically just need to "extract" a read-only interface from the JDefinedClass, this should be possible.

2 should be easier. For each com.acme.impl.Foo you'll need to add implements com.acme.Foo based on the package mapping com.acme.impl -> com.acme.

In practice you'll probably meet all kinds of problems for instance with generics etc., but this can probably not avoided with this task.

If you want a deeper dive, look at BeanGenerator and FieldRendererFactory. Those are responsible for generating outlines out of the model. So if you'd like to work on a deeper level (and not just on the CodeModel surface), you'd probably have to implement your own generator and field renderer factory. But that might be quite messy. Many central XJC classes in this area are package-protected so you'd either have to copy-paste or work around that. I think, working on the CodeModel level should be sufficient.



Related Topics



Leave a reply



Submit