I would expect the following test to work with Sun's JAXB RI 2.2.1.1, but instead it fails with a NullPointerException in constructing the JAXBContext:
public class GenericFieldMarshallTest {
public static class CustomType {
}
public static class CustomTypeAdapter extends XmlAdapter<String, CustomType> {
@Override
public String marshal(CustomType v) throws Exception {
return "CustomType";
}
@Override
public CustomType unmarshal(String v) throws Exception {
return new CustomType();
}
}
@XmlJavaTypeAdapter(type = CustomType.class, value = CustomTypeAdapter.class)
public static class RootElement<ValueType> {
@XmlValue public ValueType value;
}
@XmlRootElement(name = "root")
public static class CustomRootElement extends RootElement<CustomType> {
public CustomRootElement() {
value = new CustomType();
}
}
@Test
public void test() throws Exception {
JAXBContext context = JAXBContext.newInstance(CustomRootElement.class,
CustomType.class, RootElement.class);
StringWriter w = new StringWriter();
context.createMarshaller().marshal(new CustomRootElement(), w);
assertThat(w.toString(), equalTo("<root>CustomType</root>"));
}
}
The exception I get is:
java.lang.NullPointerException
at com.sun.xml.bind.v2.runtime.reflect.TransducedAccessor.get(TransducedAccessor.java:165)
at com.sun.xml.bind.v2.runtime.property.ValueProperty.<init>(ValueProperty.java:77)
at com.sun.xml.bind.v2.runtime.property.PropertyFactory.create(PropertyFactory.java:106)
at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.<init>(ClassBeanInfoImpl.java:179)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:515)
at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.<init>(ClassBeanInfoImpl.java:166)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:515)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:330)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1140)
at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:154)
at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:121)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:202)
at javax.xml.bind.ContextFinder.find(ContextFinder.java:363)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:574)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:522)
The cause seems to be that JAXB doesn't know how to marshal the declared type of my field (which I think is erased to Object at runtime?) even though at runtime I only ever set the field to types which JAXB is aware of.
How can I marshal a field whose type is generic?
(Replacing @XmlValue with @XmlAttribute does not fix the exception, nor does changing the declared type of the field to Object, though of course everything works fine if the field is declared as String, except that String is not assignable from CustomType. The placement of @XmlJavaTypeAdapter also makes no difference; in my actual code it is set on the package level in package-info.java.)