I\'m using JAXB to read and write XML. What I want is to use a base JAXB class for marshalling and an inherited JAXB class for unmarshalling. This is to allow a sender Java
Subclass Person twice, once for receiver and once for sender, and only put the XmlRootElement on these subclassses (leaving the superclass, Person
, without an XmlRootElement). Note that sender and receiver both share the same JAXB base classes.
@XmlRootElement(name="person")
public class ReceiverPerson extends Person {
// receiver specific code
}
@XmlRootElement(name="person")
public class SenderPerson extends Person {
// sender specific code (if any)
}
// note: no @XmlRootElement here
public class Person {
// data model + jaxb annotations here
}
[tested and confirmed to work with JAXB]. It circumvents the problem you note, when multiple classes in the inheritance hierarchy have the XmlRootElement annotation.
This is arguably also a neater and more OO approach, because it separates out the common data model, so it's not a "workaround" at all.
I am not sure why you would want to do this... it doesn't seem all that safe to me.
Consider what would happen in ReceiverPerson has additional instance variables... then you would wind up with (I guess) those variables being null, 0, or false... and what if null is not allowed or the number must be greater than 0?
I think what you probably want to do is read in the Person and then construct a new ReceiverPerson from that (probably provide a constructor that takes a Person).
Create a custom ObjectFactory to instantiate the desired class during unmarshalling. Example:
JAXBContext context = JAXBContext.newInstance("com.whatever.mypackage");
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setProperty("com.sun.xml.internal.bind.ObjectFactory", new ReceiverPersonObjectFactory());
return unmarshaller;
public class ReceiverPersonObjectFactory extends ObjectFactory {
public Person createPerson() {
return new ReceiverPerson();
}
}
Since you really have two separate apps, compile them with different versions of the class "Person" - with the receiver app not having @XmlRootElement(name="person")
on Person
. Not only is this ugly, but it defeats the maintainability you wanted from using the same definition of Person for both sender and receiver. Its one redeeming feature is that it works.
The following snippet is a method of a Junit 4 test with a green light:
@Test
public void testUnmarshallFromParentToChild() throws JAXBException {
Person person = new Person();
int age = 30;
String name = "Foo";
person.name = name;
person.age= age;
// Marshalling
JAXBContext context = JAXBContext.newInstance(person.getClass());
Marshaller marshaller = context.createMarshaller();
StringWriter writer = new StringWriter();
marshaller.marshal(person, writer);
String outString = writer.toString();
assertTrue(outString.contains("</person"));
// Unmarshalling
context = JAXBContext.newInstance(Person.class, RecieverPerson.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
StringReader reader = new StringReader(outString);
RecieverPerson reciever = (RecieverPerson)unmarshaller.unmarshal(reader);
assertEquals(name, reciever.name);
assertEquals(age, reciever.age);
}
The important part is the use of the JAXBContext.newInstance(Class... classesToBeBound)
method for the unmarshalling context:
context = JAXBContext.newInstance(Person.class, RecieverPerson.class);
With this call, JAXB will compute a reference closure on the classes specified and will recognize RecieverPerson
. The test passes. And if you change the parameters order, you'll get a java.lang.ClassCastException
(so they must be passed in this order).
You're using JAXB 2.0 right? (since JDK6)
There is a class:
javax.xml.bind.annotation.adapters.XmlAdapter<ValueType,BoundType>
which one can subclass, and override following methods:
public abstract BoundType unmarshal(ValueType v) throws Exception;
public abstract ValueType marshal(BoundType v) throws Exception;
Example:
public class YourNiceAdapter
extends XmlAdapter<ReceiverPerson,Person>{
@Override public Person unmarshal(ReceiverPerson v){
return v;
}
@Override public ReceiverPerson marshal(Person v){
return new ReceiverPerson(v); // you must provide such c-tor
}
}
Usage is done by as following:
@Your_favorite_JAXB_Annotations_Go_Here
class SomeClass{
@XmlJavaTypeAdapter(YourNiceAdapter.class)
Person hello; // field to unmarshal
}
I'm pretty sure, by using this concept you can control the marshalling/unmarshalling process by yourself (including the choice the correct [sub|super]type to construct).