Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

InstantiationException during JAXB Unmarshalling (abstract base class, with @XmlSeeAlso concrete sub class)

I am running into JAXB Unmarshalling error as below. The foo.bar.Base is an abstract class, with an @XmlSeeAlso annotation, which lists foo.bar.SubBase (which is a concrete subclass of foo.bar.Base)

Both of the above classes are statically reachable from a main/entry class: com.example.Request

The JAXBContext is create using the packages string variant viz:

JAXBContext.newInstance("com.example",...);

The above created JAXBContext correctly lists all the three classes : com.example.Request, foo.bar.Base and foo.bar.SubBase as "classes known to this JAXBContext"

But it fails at runtime during the unmarshal call below.. I am unable to figure out what is wrong here.

unmarshaller.unmarshal(<some-DOM-Element-Instance>, com.example.Request.class);

Any pointers will be appreciated! Thanks!

The stacktrace is:

    Caused by: javax.xml.bind.UnmarshalException: Unable to create an instance of foo.bar.Base  - with linked exception: [java.lang.InstantiationException]

    at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:642)

    at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:254)

    at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.createInstance(UnmarshallingContext.java:609)

    at com.sun.xml.bind.v2.runtime.unmarshaller.StructureLoader.startElement(StructureLoader.java:181)

    at com.sun.xml.bind.v2.runtime.unmarshaller.XsiTypeLoader.startElement(XsiTypeLoader.java:76)

    at com.sun.xml.bind.v2.runtime.unmarshaller.ProxyLoader.startElement(ProxyLoader.java:55)

    at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:481)

    at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:459)

    at com.sun.xml.bind.v2.runtime.unmarshaller.InterningXmlVisitor.startElement(InterningXmlVisitor.java:71)

    at com.sun.xml.bind.v2.runtime.unmarshaller.SAXConnector.startElement(SAXConnector.java:148)

    at com.sun.xml.bind.unmarshaller.DOMScanner.visit(DOMScanner.java:239)

    at com.sun.xml.bind.unmarshaller.DOMScanner.visit(DOMScanner.java:276)

    at com.sun.xml.bind.unmarshaller.DOMScanner.visit(DOMScanner.java:245)

    at com.sun.xml.bind.unmarshaller.DOMScanner.scan(DOMScanner.java:122)

    at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:314)

    at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:293)

Caused by: java.lang.InstantiationException

    at sun.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(InstantiationExceptionConstructorAccessorImpl.java:30)

    at java.lang.reflect.Constructor.newInstance(Constructor.java:513)

    at com.sun.xml.bind.v2.ClassFactory.create0(ClassFactory.java:123)

    at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.createInstance(ClassBeanInfoImpl.java:261)

    at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.createInstance(UnmarshallingContext.java:603)

    ... 69 more

EDIT @Blaise and @Ross

Thank you very much Blaise and Ross for your pointers. I think I should have included the schema that is being worked off, here. The relevant schema looks like this:

    <xs:complexType name="Request">
                    <xs:sequence>
                        <xs:element name="selectedBase" form="unqualified" nillable="true" type="xs:anyType" minOccurs="0"/>
                        <xs:element name="selectedSubBase" form="unqualified" nillable="true" type="ns1:SubBase" minOccurs="0"/>
                    </xs:sequence>
                </xs:complexType>


<xs:complexType name="Base">
                <xs:sequence>
                    <xs:element name="ID" form="unqualified" nillable="true" type="xs:string" minOccurs="0"/>
                </xs:sequence>
            </xs:complexType>
            <xs:complexType name="SubBase">
                <xs:complexContent>
                    <xs:extension base="ns1:Base">
                        <xs:sequence>
                            <xs:element name="subBaseElement" form="unqualified" nillable="true" type="xs:anyType" minOccurs="0"/>
                        </xs:sequence>
                    </xs:extension>
                </xs:complexContent>
            </xs:complexType>

So the schema doesn't have substitution group definition (so I guess @XmlElementRef doesn't apply here, or would it still work?), but is using extension. The payload will be :

<ns:Request>
         <selectedBase>123</selectedBase>
         <selectedSubBase>
            <ID>321</ID>
            <subBaseElement>123</subBaseElement>
         </selectedSubBase>
      </ns:Request>

So , the element in the payload occuring is <selectedSubBase> and not <selectedBase xsi:type="ns:SubBase"/>

So which strategy would apply here?

like image 987
Anand Avatar asked Sep 14 '11 11:09

Anand


2 Answers

The @XmlSeeAlso annotation is used as a convenience mechanism to tell your JAXB impl that metadata should also be created for the referenced classes. While it is most often used to specify subclasses it is not a mechanism to configure inheritance relationships.

Since JAXB is attempting to instantiate an instance of the abstract super class (foo.bar.Base), it appears as though your XML message does not contain enough information to specify the correct sub-type to be unmarshalled.

This can be done with the xsi:type attribute:

  • http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-xsitype.html

You can also use substitution groups (@XmlElementRef), where the element name is used to determine the appropriate sub-type:

  • http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-substitution.html

JAXB implementations (such as EclipseLink JAXB (MOXy)), also contain extensions for handling inheritance:

  • http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-moxy-extension.html

And if you would like to ignore the inheritance relationship altogether you can use the @XmlTransient annotation:

  • http://blog.bdoughan.com/2011/06/ignoring-inheritance-with-xmltransient.html
like image 63
bdoughan Avatar answered Oct 15 '22 04:10

bdoughan


You need to be using XmlElementRef on the field containing the reference to Base, to tell JAXB that it should look at subclasses. JAXB is clearly trying to instantiate your base class (which it can't do, of course).

Have a look at XmlElementRef's docs.

like image 38
Ross Judson Avatar answered Oct 15 '22 05:10

Ross Judson