84tamer
84tamer

Reputation: 13

JAXB generating unwanted @XMLValue

Been grappling with a problem for about half a week now, and need help as I cannot seem to make any headway.

I've inherited an application which hasn't been treated nicely for 8 years....still on Java 1.4, Maven1 build, no new unit tests for 8 years...

Currently the upgrade to Java 1.6 (Java 1.8 branch also done in parallel, will test both) and Maven 3.3.3 is well in swing - have been making excellent headway.

Now I've hit a wall and not made a breakthrough for a while.

The old sources used local JAXB 1.3 jars to generate classes from a large XSD.

I had to migrate from JAXB1.3 to JAXB2.1 - which also meant i had to spend a lot of time rewriting all the references to the generated classes as the naming conventions changed. Anyway, a lot of time was spent getting the code to compile.

Finally, it compiles, and I try out a unit test to see how it works. This is where i hit my problem.

Most of the classes compiled work fine, but three of the packages throw exceptions when i try to generate the JAXBContext:

@XmlValue is not allowed on a class that derives another class.

I've narrowed the problem down to a pattern which occurs in a couple of the generated classes. The class that causes the exception is defined in the schema as below:

    <xs:element name="ContactName">
    <xs:complexType>
        <xs:simpleContent>
            <xs:extension base="xs:string">
                <xs:attribute name="First" type="xs:string"/>
                <xs:attribute name="Middle" type="xs:string"/>
                <xs:attribute name="Last" type="xs:string"/>
                <xs:attribute name="Name" type="xs:string"/>
            </xs:extension>
        </xs:simpleContent>
    </xs:complexType>
</xs:element>

and then this element is referenced in another as follows:

<xs:element name="ContactInfo">
    <xs:complexType>
        <xs:annotation>
            <xs:documentation>Common contact information</xs:documentation>
        </xs:annotation>
        <xs:sequence>
            <xs:element ref="ContactName" minOccurs="0" maxOccurs="unbounded"/>
            <xs:element ref="ContactID" minOccurs="0"/>
            <xs:element ref="ContactDivision" minOccurs="0" maxOccurs="unbounded"/>
            .....

this is generated into:

ContactName:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "value"
})
@XmlRootElement(name = "ContactName")
public class ContactName
    extends BaseJaxbDoc
    implements Serializable, Cloneable, CopyTo
{

    private final static long serialVersionUID = 47110815L;
    @XmlValue
    protected String value;
    @XmlAttribute(name = "First")
    protected String first;
    @XmlAttribute(name = "Middle")
    protected String middle;
    @XmlAttribute(name = "Last")
    protected String last;
    @XmlAttribute(name = "Name")
    protected String name;

And then declared in ContactInfo as follows:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "contactName",
    "contactID",
    "contactDivision",
    "contactPhone",
    "contactPhoneHome",
    "contactPhoneMobile",
    "contactFax",
    "contactEmail",
    "contactEmail2"
})
@XmlRootElement(name = "ContactInfo")
public class ContactInfo
    extends BaseJaxbDoc
    implements Serializable, Cloneable, CopyTo
{

    private final static long serialVersionUID = 47110815L;
    @XmlElement(name = "ContactName")
    protected List<ContactName> contactName;

The exception thrown is at:

this problem is related to the following location:
    at protected java.lang.String xxx.xx.xxxx.xxxx.orders.jaxb.ContactName.value
    at xxx.xx.xxxx.xxxx.orders.jaxb.ContactName
    at protected java.util.List xxx.xx.xxxx.xxxx.orders.jaxb.ContactInfo.contactName
    at xxx.xx.xxxx.xxxx.orders.jaxb.ContactInfo
    at protected java.util.List xxx.xx.xxxx.xxxx.orders.jaxb.CustomerReference.contactInfo
    at xxx.xx.xxxx.xxxx.orders.jaxb.CustomerReference
    at protected java.util.List xxx.xx.xxxx.xxxx.orders.jaxb.Item.customerReference
    at xxx.xx.xxxx.xxxx.orders.jaxb.Item
    at public xxx.xx.xxxx.xxxx.orders.jaxb.Item xxx.xx.xxxx.xxxx.orders.jaxb.ObjectFactory.createItem()
    at xxx.xx.xxxx.xxxx.orders.jaxb.ObjectFactory

There is an XML transformation on the original schema, stripping comments out and creating jaxb:typesafeEnum types. Then the transformed schema is used with a jxb binding file to bind everything to an internal jaxb helper superclass - BaseJaxbDoc

        <jaxb:globalBindings generateIsSetMethod="true">
        <xjc:serializable uid="47110815"/>
        <xjc:superClass name="xxx.xx.xxxx.xxxx.helpers.BaseJaxbDoc"/>
        <jaxb:javaType name="java.math.BigDecimal" xmlType="xs:decimal" 
             parseMethod="xxx.xx.xxxx.xxxx.helpers.AmountConverter.parseAmount" 
            printMethod="xxx.xx.xxxx.xxxx.helpers.AmountConverter.printAmount"/>
        </jaxb:globalBindings>

This is because I am using xjc on 9 different schemas, all generating JAXB packages of classes.

The classes all have the same superclass (defined in a bindings file for each schema) to only implement the JAXB marshall/unmarshall classes once, along with some other helper functions. So my question is how to get around this exception when i cannot modify the schema? Something in the XSLT or something in the bindings file?

My Maven dependencies: for JAXB: org.jvnet.jaxb2.maven2 maven-jaxb21-plugin 0.13.0

JAXB runtime: org.glassfish.jaxb jaxb-runtime 2.2.11

Upvotes: 1

Views: 1936

Answers (1)

lexicore
lexicore

Reputation: 43671

Try annotating BaseJaxbDoc with @XmlTransient.

The problem you're getting is produced here:

                if(getBaseClass()!=null) {
                    builder.reportError(new IllegalAnnotationException(
                        Messages.XMLVALUE_IN_DERIVED_TYPE.format(), p ));
                }

JAXB thinks your BaseJaxbDoc is a base class. So you should either remove xjc:superClass or trink JAXB into thinking that your class does not have a base class.

When I look at this part of the code in the ModelBuilder:

        if(reader.hasClassAnnotation(clazz,XmlTransient.class) || isReplaced) {
            // handle it as if the base class was specified
            r = getClassInfo( nav.getSuperClass(clazz), searchForSuperClass,
                    new ClassLocatable<C>(upstream,clazz,nav) );
        }

It seems that the ModelBuilder recognizes @XmlTransient on classes and does not consider them. So there's a chance that assing @XmlTransient on your BaseJaxbDoc would help.

Another option is to drop BaseJaxbDoc construct. You use class inheritance to add marshal/unmarshal functionality to the schema-derived classes. I'd rather move this functionality out into some external services. This is probably not an option here as you're probably facing a lot of legacy code.

A further option is to try MOXy instead of JAXB RI in the runtime.

Upvotes: 1

Related Questions