I am having an issue with using inheritance and JAXB unmarshalling. I have read the number of examples (specifically a much-reference blog at http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-xsitype.html and a very similar SO question here: JAXB xsi:type subclass unmarshalling not working) and am still having difficulties.
Like many of these other questions, I am trying to create an XML representation of an object whose fields rely on subclasses for information. I do not know at compile time what the concrete subclass implementation will be, so things like XmlSeeAlso is not really available.
In my test case I have a Root class with an abstract class (A) that has a concrete subtype (B):
@XmlRootElement
@ToString
public class Root {
private A theClass;
public A getTheClass() {
return theClass;
}
public void setTheClass(A theClass) {
this.theClass = theClass;
}
}
@ToString
public abstract class A {
}
@XmlRootElement
@ToString
public class B extends A {
private String b = "from B-" + System.currentTimeMillis()
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
}
Where @ToString are annotations from project Lombok.
I can marshall with no problem:
@Test
public void marshallTest() {
try {
Root r = new Root();
B b = new B();
r.setTheClass(b);
Class<?>[] classArray = new Class<?> [] { Root.class, B.class };
JAXBContext context = JAXBContext.newInstance(classArray);
Marshaller marshaller = context.createMarshaller();
try (FileWriter fw = new FileWriter(JAXB_TEST_XML)) {
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(r, fw);
}
try(StringWriter sw = new StringWriter() ) {
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(r, sw);
log.debug("{}", sw.toString());
}
} catch (IOException | JAXBException e) {
e.printStackTrace();
fail(e.getMessage());
}
}
Which produces the following xml:
<root>
<theClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="b">
<b>from B-1375211003868</b>
</theClass>
</root>
When I try to unmarshall (using MOXY JAXB implemntation) I get:
This class does not define a public default constructor, or the constructor raised an exception.
Internal Exception: java.lang.InstantiationException
Descriptor: XMLDescriptor(xml.A --> [])
With the following code:
@Test
public void unmarshall() {
try {
JAXBContext context = JAXBContext.newInstance(new Class<?>[] {Root.class, A.class});
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
DocumentBuilder db = documentBuilderFactory.newDocumentBuilder();
Root r = null;
try(BufferedInputStream bis = new BufferedInputStream(new FileInputStream(JAXB_TEST_XML))) {
Document doc = db.parse(bis);
Unmarshaller unmarshaller = context.createUnmarshaller();
JAXBElement<?> result = unmarshaller.unmarshal(doc, Root.class);
r = (Root) result.getValue();
}
assertTrue(r != null & r.getTheClass() != null && r.getTheClass() instanceof B);
} catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
}
I have tried making the unmarshalling namespace aware (as with JAXB xsi:type subclass unmarshalling not working which has not worked. I have tried using XmlElementRef which does not work either. I have tried the latest glassfish api and implementations downloaded from maven central (2.2.8). I have tried the MOXY eclipse persistence JAXB implementation. Neither have worked. I have tried unmarshalling without using a document builder which does not work as well. I have tried passing the Root.class and A.class into the JAXB context which also does not work.
I have a feeling that I have a fundamental misconception about what is going on. Any hints or ideas would be appreciated. Thank you.