Search code examples
springxsdjaxbpojo

JAXB use element name instead of type when generating pojo


With jaxb I want to generate pojos from xsd. But the xsd is provided by external vendor, where element have meaninful names, but types are weird. Just an example:

<xs:element name="PersonAddress" type="PerAdr" />

<xs:complexType name="PerAdr">
    <xs:sequence>
        <xs:element name="street" type="xs:string" minOccurs="1" maxOccurs="unbounded" />
        <xs:element name="house" type="xs:string" minOccurs="1" maxOccurs="unbounded" />
    </xs:sequence>
</xs:complexType>

So generated class is called PerAdr. How to make it generating classes where their names are element names, not type so it would generate in this case class PersonAddress.

I have a huge xsd, so thinking about a clever way of doing it, not just writing hundreds of lines in .xjb file


Solution

  • I am not exactly a professional in JAXB. But I've looked at JAXB specification (here: http://download.oracle.com/otn-pub/jcp/jaxb-2.0-fr-oth-JSpec/jaxb-2_0-fr-spec.pdf) and found the following:


    The characteristics of the schema-derived Element class are derived in terms of the properties of the “Element Declaration Schema Component” on page 349 as follows:

    • The name of the generated Java Element class is derived from the element declaration {name} using the XML Name to Java identifier mapping algorithm for class names.

    • Each generated Element class must extend the Java value class javax.xml.bind.JAXBElement<T>. The next bullet specifies the schema-derived Java class name to use for generic parameter T.

    • If the element declaration’s {type definition} is

      • Anonymous: Generic parameter T from the second bullet is set to the schema-derived class represented the anonymous type definition generated as specified in Section 6.7.3.
      • Named: Generic parameter T from the second bullet is set to the Java class representing the element declaration’s {type definition}.

    So, one may conclude from that: Once you have an XSD element with a named XSD type, you've got to deal with a Java class representing that type and named after it. That's logical. After all, you may have different XSD elements with the same global type. That's the default mapping.

    However, JAXB allows for customizations (of the XML schema), with which you can override certain things. For instance, you can modify the name of the Java class generated by the XSD type, e.g:

    <xs:complexType name="USAddress">
      <xs:annotation> <xs:appinfo>
        <jaxb:class name="MyAddress" />
      </xs:appinfo></xs:annotation>
      <xs:sequence>...</xs:sequence>
      <xs:attribute name="country" type="xs:string"/>
    </xs:complexType>
    

    So, instead of USAddress, the result Java class will be named MyAddress. This looks like a solution to your problem, but to take advantage of it, you will need to modify every type definition in your XSD, which sounds daunting because your schema is huge.

    So, what can you do?

    First of all, you need to make sure that each XSD element in your schema and its (globally defined) type uniquely correspond each other. If there happen to be several different XSD elements with the same type, obviously the type name cannot be equal to all of them. In that case, if you don't like the original type names, you just need to edit that schema manually and give those types different names as it better fits to you.

    Any automation is possible only when the relation XSD element <-> its XSD type is unique! In that case, you can derive the type name from the element name: make it the same or add, for instance, T prefix: TPersonAddress.

    That is typically called refactoring and can be done automatically. The question is how?

    Well, since XSD is XML, you can write an XSLT script that does the necessary transformation. But that may be not so straightforward, because you will have to parse the schema a bit. That is, to recognize every XSD element there and find the corresponding XSD type, and then to change the type name at both locations. Alternatively, you can insert those customization directives (<jaxb:...> elements) within the definition of each XSD type, as mentioned above. I don't know how much it would take to program such things. That will definitely come down to the creation of an index (with <xsl:key> construct) and iterating by it.


    Alternatively, I can suggest you some unorthodox approach. We have developed a tool called FlexDoc/XML. Essentially, it is a transformer of XML files into anything. The transformation is programmed using some templates that work similar to XSLT.

    The original idea was to extend that XSLT-like approach to any kind of Java based data-sources provided via various APIs. For instance, we have a similar product called FlexDoc/Javadoc that mimics standard Javadoc. But then we realized that XML itself is also a good field full of various heavy-lifting tasks, for which XSLT is too lightweight. For instance, the generation of easily navigable single documentation by hundreds of XSD and WSDL files, for which we have two template sets now: XSDDoc and WSDLDoc. (We are working also on a similar thing for JSON Schemas).

    Using FlexDoc/XML it is possible to create a template that does what you need (renaming those XSD types). That can be accomplished in a matter of a hour, and we will do it for you, if you purchase eventually a "FlexDoc/XML SDK" license. (People typically buy the SDK license to customize XSDDoc/WSDLDoc templates. But it can be equally used as a separate tool for the tasks like yours.)