Reputation: 136
Based on the following program, it prints out what we expect it to correctly. Is it possible for such a program to unmarshall correctly if the classes ClassA and ClassB used the same XmlRootElement name? For example, if they were both defined as "typeA"...? Would it be possible to do something like that with JAXB?
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.*;
import java.io.ByteArrayOutputStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.Date;
public class JaxbABCTest {
public static void main(String[] args) throws JAXBException, UnsupportedEncodingException {
final JAXBContext context = JAXBContext.newInstance(ABC.class);
final Marshaller marshaller = context.createMarshaller();
final Unmarshaller unmarshaller = context.createUnmarshaller();
ABC class1 = new ClassA();
ABC class2 = new ClassB();
final ByteArrayOutputStream baosA = new ByteArrayOutputStream();
final ByteArrayOutputStream baosB = new ByteArrayOutputStream();
// Marshall to XML
marshaller.marshal(class1, baosA);
marshaller.marshal(class2, baosB);
String xmlA = baosA.toString(Charset.defaultCharset().name());
String xmlB = baosB.toString(Charset.defaultCharset().name());
System.out.println(xmlA);
System.out.println(xmlB);
// Now attempt the reverse.
Object unmarshalA = unmarshaller.unmarshal(new StringReader(xmlA));
Object unmarshalB = unmarshaller.unmarshal(new StringReader(xmlB));
System.out.println(unmarshalA.getClass());
System.out.println(unmarshalB.getClass());
}
}
@XmlTransient
@XmlSeeAlso({
ClassA.class,
ClassB.class
})
abstract class ABC {
private int a;
@XmlAttribute
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
}
@XmlRootElement(name = "typeA")
class ClassA extends ABC {
private String b;
private Date c;
@XmlAttribute
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
@XmlAttribute
public Date getC() {
return c;
}
public void setC(Date c) {
this.c = c;
}
}
@XmlRootElement(name = "typeB")
class ClassB extends ABC {
private String b;
private Date c;
private boolean d;
private float e;
@XmlAttribute
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
@XmlAttribute
public Date getC() {
return c;
}
public void setC(Date c) {
this.c = c;
}
@XmlAttribute
public boolean isD() {
return d;
}
public void setD(boolean d) {
this.d = d;
}
@XmlAttribute
public float getE() {
return e;
}
public void setE(float e) {
this.e = e;
}
}
Upvotes: 0
Views: 993
Reputation: 31290
For straightforward JAXB unmarshalling, (root) one element name refers to an element with a specific structure which is mapped to one certain Java class.
You can, of course, define a class containing the union of the fields of A and B - as long as fields occuring in both classes match. You'll have to have some attribute that lets you decide which of the two "subclasses" it really is. (This is similar in spirit to using an XPath expression that includes a test for the presence (or even value) of an attribute or element.)
You may also pursue a more elaborate approach leaving this element unmarshalled after reading the XML, investigate the DOM tree and create a JAXBContext according to what has been detected. This will let you use the same element name with Java types exactly matching the XML content. Of course, an unambiguous criterion must be available, and youÄll have to write the code for analysing based on te raw DOM tree data.
Upvotes: 1