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?
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:
You can also use substitution groups (@XmlElementRef
), where the element name is used to determine the appropriate sub-type:
JAXB implementations (such as EclipseLink JAXB (MOXy)), also contain extensions for handling inheritance:
And if you would like to ignore the inheritance relationship altogether you can use the @XmlTransient
annotation:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With