Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JAXB generated classes of certain types implementing a custom interface

Tags:

jaxb

xjc

jaxb2

scd

I am working on an application that uses XJC to generate Java POJOs from XSDs. There are dozens of schemas, and that number will grow. The application also needs to be able to handle different versions of the same schema, which means that I will have multiple schemas defining common types. I am trying to customize the bindings so that certain core types implement a common interface. The Inheritance plugin of JAXB2 Basics seems to do what I need, but I can't seem to nail the right syntax.

Here is the relevant part of my schema:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:my="http://example.com/core"
           targetNamespace="http://example.com/core"
           xmlns:xml="http://www.w3.org/XML/1998/namespace">

    ...

    <xs:complexType name="addressType">
        <xs:sequence>
            <xs:element name="Address" type="xs:string"/>
            <xs:element name="City" type="xs:string"/>
            <xs:element name="Province" type="xs:string"/>
            <xs:element name="Country" type="xs:string"/>
            <xs:element name="County" type="xs:string" minOccurs="0"/>
            <xs:element name="PostalCode" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>

    ...

</xs:schema>

... and this is what my custom binding file looks like:

    <?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://example.com/core"
    jaxb:extensionBindingPrefixes="inheritance"
    version="2.1">

    <jaxb:bindings scd="x-schema::my" xmlns:my="http://example.com/core">
        <jaxb:globalBindings localScoping="toplevel">
            <jaxb:serializable/>
            <xjc:simple/>
        </jaxb:globalBindings>
        <jaxb:bindings scd="/type::my:addressType">
            <inheritance:implements>com.mysite.validator.ValidatableAddress</inheritance:implements> 
            <!--<xjc:superInterface name="com.mysite.validator.ValidatableAddress"/>-->
        </jaxb:bindings>
    </jaxb:bindings>

</jaxb:bindings>

I'm using the scd approach, because in all the "traditional" binding examples that show how to use the inheritence plugin, schemaLocation is specified. I want to avoid having to specify schemaLocation because of our large (and growing) number of schemas. I don't want to have to change the binding file every time we add a new schema. So, scd seems like it will satisfy this requirement.

However when I run the build using the above binding, I get this:

  [xjc] [ERROR] cvc-elt.1: Cannot find the declaration of element 'inheritance:implements'.
  [xjc]   line 18 of file:/dev/workspace/my_app/etc/schemas/bindings-common.xml
  [xjc] failure in the XJC task. Use the Ant -verbose switch for more details
  [xjc] classLoader = java.net.URLClassLoader@ebcdbb
  [xjc] SharedSecrets.getJavaNetAccess()=java.net.URLClassLoader$7@14562c5

If I comment out the inheritance:implements line and uncomment the xjc:superInterface line, the error goes away and the build completes successfully, but my AddressType classes don't implement the ValidatableAddress type.

Can the inheritence plugin be used with scd? Can xjc:superInterface be limited to only certain elements?

Cheers.

like image 667
6006604 Avatar asked Jun 16 '15 13:06

6006604


1 Answers

Author of jaxb2-basics here.

See this issue in XJC. In short, XJC for some reason does not allow custom/vendor customization elements in SCD bindings.
inheritance:implements is such customization element.

So no, this does not work due a problem in XJC.

I personally bind via schemaLocation and XPath but use a "virtual" schema location URI and rewrite it via catalogs.

SCD would have been a much better choice (you're absolutely right here) but it just does not work.

Update on virtual schema location and catalogs.

Here's an example of binding which customizes some complex type:

<jaxb:bindings 
    schemaLocation="http://schemas.opengis.net/wps/2.0/wpsCommon.xsd" 
    node="/xs:schema">
    <jaxb:bindings node="xs:element[@name='Data']/xs:complexType">
        <wildcard:lax/>
    </jaxb:bindings>
</jaxb:bindings>

It is bound via schemaLocation and XPath. The schemaLocation is an existing URL, but in the build it is rewritten via catalog into the resource inside a Maven artifact:

REWRITE_SYSTEM "http://schemas.opengis.net" "maven:org.jvnet.ogc:ogc-schemas:jar::!/ogc"

So basically http://schemas.opengis.net/wps/2.0/wpsCommon.xsd will be loaded from the ogc-schema.jar!/ogc/wps/2.0/wpsCommon.xsd.

Using maven-jaxb2-plugin you can refer to binding files inside Maven artifacts as well:

                    <binding>
                        <dependencyResource>
                            <groupId>${project.groupId}</groupId>
                            <artifactId>ows-v_2_0</artifactId>
                            <resource>ows-v_2_0.jsonix.xjb</resource>
                            <version>${project.version}</version>
                        </dependencyResource>
                    </binding>

So, in combination, it allows writing binding files once and reuse them across modules.

But this was all a huge pain to figure out. I did it for the ogc-schemas project which is now some 50 heavily interconnected schemas. I heavily suffer from XJC drawbacks and issues, but this is the best possible at the moment. I even thought about forking and patching XJC, but this is far of effort limits I can afford.

So I've ended up with a number of mindblogging workarounds which somehow do the job at the end of the day.

like image 104
lexicore Avatar answered Sep 23 '22 00:09

lexicore