I have a situation in which a Java object contains a generic Payload<T> that needs to be marshalled into xml. So given the following classes:
AbstractBox
@XmlTransient
public abstract class AbstractBox {
String type = this.getClass().getSimpleName();
String name = this.getClass().getCanonicalName();
// setters/getters snipped
public abstract String saySomething();
}
SmallBox
@XmlRootElement(name="small-box")
@XmlType(name="small-box")
public class SmallBox extends AbstractBox {
@XmlElement
public String getMsg() {
return saySomething();
}
public void setMsg(String msg) {
// do nothing
}
@Override
public String saySomething() {
return "I'm a small box";
}
}
Payload
@XmlTransient
public class Payload<T> {
private T payload;
public T getPayload() {
return payload;
}
public void setPayload(T payload) {
this.payload = payload;
}
}
and some code like this:
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class JaxbAnnotationsTest<P>
{
String name;
int age;
int id;
Payload<P> payload;
// setters and getters snipped
public static void main(String[] args) {
JaxbAnnotationsTest<AbstractBox> example = new JaxbAnnotationsTest<AbstractBox>();
example.setName("Brion");
example.setId(100);
example.setAge(34);
Payload<AbstractBox> object = new Payload<AbstractBox>();
object.setPayload(new SmallBox());
example.setPayloadContainer(object);
try {
XmlMapper xmapper = new XmlMapper();
xmapper.writeValue(System.out, example);
} catch (Exception ex) {
System.err.println("Damn..." + ex.getMessage());
}
}
I expect to see:
<JaxbAnnotationsTest>
<name>Brion</name>
<age>34</age>
<id>100</id>
<payloadContainer>
<small-box>
<type>SmallBox</type>
<name>sandbox.xml.jaxb.annotations.SmallBox</name>
<msg>I'm a small box</msg>
</small-box>
</payloadContainer>
</JaxbAnnotationsTest>
but instead I get:
<JaxbAnnotationsTest>
<name>Brion</name>
<age>34</age>
<id>100</id>
<payloadContainer>
<payload>
<type>SmallBox</type>
<name>sandbox.xml.jaxb.annotations.SmallBox</name>
<msg>I'm a small box</msg>
</payload>
</payloadContainer>
</JaxbAnnotationsTest>
I've tried using @XmlType on the concrete subclass to change payload to small-box but that didn't work either. If I remove the Payload<P> object and simply have a class member payload of generic type P then the paymentContainer tag goes away, but payload remains and does not use the small-box name I've specified.
Is there a way for me to force JAXB (any implementation) to set the tag to the name specified in the subclass instead of the generic type property?
Update: The selected answer provides the solution but I wanted to follow up in the question as well. My problem was two-fold:
- I was using
@XmlTransienton thePayload<T>class and needed to instead use an@XmlAnyElementannotation on thesetPayload(T payload)method (though I suspect it doesn't matter which method of the setter/getter pair is annotated as long as only one has the annotation). - I was using Jackson 2's
JacksonJaxbXmlProviderwhich is an incomplete JAXB implementation that was ignoring the@XmlRootElementof the element used as the value of the@XmlAnyElement-annotated property.
Changing my JAXB provider to use the Java 6/7 built-in JAXB provider generated the output I expected.