7

Need help with following situation: Users can generate their own data structures which are stored as JAXB-ready XSD sources like below:

<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:element name="Group" type="Group"/>
  <xs:element name="Parameter" type="Parameter"/>

  <xs:complexType name="Group">
    <xs:sequence>
      <xs:element name="caption" type="xs:string" minOccurs="0"/>
      <xs:element name="parameters" type="Parameter" nillable="true" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="Parameter">
    <xs:sequence>
      <xs:element name="key" type="xs:string" minOccurs="0"/>
      <xs:element name="group" type="Group" minOccurs="0"/>
      <xs:element name="value" type="xs:string" minOccurs="0"/>
    </xs:sequence>
  </xs:complexType>
</xs:schema>

After new or modified schema appears it is automatically parsed by Schema compiler, java sources being generated, compiled and packaged into user jars:

  SchemaCompiler sc = XJC.createSchemaCompiler();
  // Input source for schema
  InputSource is = new InputSource(new StringInputStream(objectPackage.getObjectSchema()));
  // Parse
  sc.parseSchema(is);
  S2JJAXBModel model = sc.bind();
  // Generate source
  JCodeModel jCodeModel = model.generateCode(null, null);
  jCodeModel.build(packageSourceDirectory);
  // Compile and package 
  // ......

And everything was ok until it was decided that all user-generated classes must extend one specific known class, say UserRootObject:

package user.abc;
public class Group extends com.mycompany.xml.UserRootObject {
  //
}

and

package user.abc;
public class Parameter extends com.mycompany.xml.UserRootObject {
  //
}

Everything is on the fly, I can not force users to modify their schema files but I can transform them prior to code generation. Looks like I have two options to introduce that UserRootObject: somehow via JCodeModel or somehow transforming schema files before building Java sources.

tshepang
  • 12,111
  • 21
  • 91
  • 136
andbi
  • 4,426
  • 5
  • 45
  • 70

4 Answers4

8

XJC has an extension for this purpose

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
           xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
           jaxb:extensionBindingPrefixes="xjc"
           jaxb:version="2.0">

    <xs:annotation>
       <xs:appinfo>
          <jaxb:globalBindings>
           <xjc:superClass name="com.mycompany.xml.UserRootObject"/>
          </jaxb:globalBindings>
       </xs:appinfo>
    </xs:annotation>
.
.
.
</xs:schema>

For more information see:

The schema annotations can also be supplied via an external bindings file. For an example see:

Community
  • 1
  • 1
bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • Nice solution. Wish you were here in 2010 :) Personally I prefer to stick with JAXB spec and not rely on XJC implementation, but the solution might be very useful and time saving for others. Btw that "-extension" mode mentioned is a bit confusing, one will have to check whether this mode is enabled for on-the-fly schema compilation like in my case. – andbi Jan 05 '11 at 19:50
6

Many thanks to D.Shawley for pointing out the right section in JSR 222. Here is final solution which might be helpful and time saving for someone else. Original schema must be transformed as follows:

    <xs:schema version="1.0" 
               xmlns:xs="http://www.w3.org/2001/XMLSchema" 
               xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" 
               jaxb:version="2.0" >

      <xs:element name="Group" type="Group"/>
      <xs:element name="Parameter" type="Parameter"/>


      <xs:complexType name="Group">
        <xs:complexContent>
          <xs:extension base="UserRootObject">
            <xs:sequence>
            <!-- params -->
            </xs:sequence>
          </xs:extension>
        </xs:complexContent>
      </xs:complexType>

      <xs:complexType name="Parameter">
        <xs:complexContent>
          <xs:extension base="UserRootObject">
            <xs:sequence>
            <!-- params -->
            </xs:sequence>
          </xs:extension>
        </xs:complexContent>
      </xs:complexType>

      <xs:complexType name="UserRootObject">
        <xs:annotation>
          <xs:appinfo>
            <jaxb:class name="UserRootObject" implClass="com.mycompany.xml.UserRootObject"/>
          </xs:appinfo>
        </xs:annotation>
      </xs:complexType>
    </xs:schema>

Transformation can be easily performed via org.w3c.dom.Document inteface.

andbi
  • 4,426
  • 5
  • 45
  • 70
1

I do not believe that there is an easy way to do this using JAXB itself. There are a number of customization options available that are not commonly known - read section 7 of JSR222 for details.

If you have some control over the input schemas, then you might want to consider using XSLT to transform the schema. I believe that this can be done by using a javax.xml.transform.dom.DOMResult instance as the target of the transformation and using the output as a DOM tree (e.g., calling getNode() on the result) as the input to parseSchema. A basic transformation would be to replace:

<xs:complexType name="foo">
  <!-- CONTENTS -->
</xs:complexType>

with something like:

<xs:complexType name="foo">
  <xs:complexContent>
    <xs:extension base="UserRootObject">
      <!-- CONTENTS -->
    </xs:extension>
  </xs:complexContent>
</xs:complexType>

Of course this only works for the simple cases. If you already have inheritance in your schema files, then you will have to do some filtering in the XSLT that only applies this transform to types that do not already extend.

D.Shawley
  • 58,213
  • 10
  • 98
  • 113
  • Thanks! Yes, I could transform schema that way but how do I tell schema compiler not to generate UserRootObject but rather 'make reference' to an existing class? Is it possible at all? That'd be great and solve the problem. – andbi Dec 29 '10 at 19:51
  • 1
    You are correct in that standard JAXB does not cover this use case, but XJC does offer this as an extension. For more details see: http://stackoverflow.com/questions/4556179/how-to-force-schema-compiled-classes-to-extend-specific-class-outside-schema/4607476#4607476 – bdoughan Jan 05 '11 at 18:04
0

It is a bit an old topic, but just for the record:

You can do this with the jaxb2 inheritance plugin. Like this:

<xs:complexType name="...">
  <xs:annotation>
    <xs:appinfo>
      <inheritance:extends>
         com.mycompany.xml.UserRootObject
      </inheritance:extends>
    </xs:appinfo>
  </xs:annotation>
  <!-- ... -->
</xs:complexType>

See for example here http://vistaofjavahorizon.blogspot.com/2014/07/how-to-have-particular-java-class.html

There are more examples online.

R Hoekstra
  • 99
  • 3