I have an app using the JAX-RS implementation of CXF. I've added JAXB
annotations to the classes I want to render. Up to a certain point, the
objects were rendering fine.
I then added a Map property and discovered I had to define an
XmlAdapter, as JAXB can't render Maps directly. I looked in the javadoc
for XmlAdapter, and I looked at the sample in the Java EE 5 tutorial. I
believe I met all the requirements, although my implementation is
slightly different. I didn't see any reason my HashMapType and
HashMapEntry couldn't be generic, although the Adapter class obviously
has to be type-specific.
After I added these changes, the marshalling process fails with a
NullPointerException. Even worse, it doesn't report a stack trace. I
only see the following output:
org.apache.cxf.jaxrs.impl.WebApplicationExceptionMapper toResponse
WARNING: WebApplicationException has been caught : cause is
java.lang.NullPointerException
I ran my test case in the debugger and had it break on a NPE. I found
that it was throwing the NPE at line 165 in class
"com.sun.xml.bind.v2.runtime.reflect.TransducedAccessor<BeanT>".
That line is in this function (decompiled from class):
/* */ public static <T> TransducedAccessor<T> get(JAXBContextImpl
context, RuntimeNonElementRef ref)
/* */ {
/* 153 */ Transducer xducer =
RuntimeModelBuilder.createTransducer(ref);
/* 154 */ RuntimePropertyInfo prop = ref.getSource();
/* */
/* 156 */ if (prop.isCollection()) {
/* 157 */ return new ListTransducedAccessorImpl(xducer,
prop.getAccessor(),
Lister.create(Navigator.REFLECTION.erasure(prop.getRawType()),
prop.id(), prop.getAdapter()));
/* */ }
/* */
/* 162 */ if (prop.id() == ID.IDREF) {
/* 163 */ return new
IDREFTransducedAccessorImpl(prop.getAccessor());
/* */ }
/* 165 */ if ((xducer.isDefault()) && (!(context.fastBoot))) {
/* 166 */ TransducedAccessor xa =
OptimizedTransducedAccessorFactory.get(prop);
/* 167 */ if (xa != null) return xa;
/* */ }
/* */
/* 170 */ if (xducer.useNamespace()) {
/* 171 */ return new
CompositeContextDependentTransducedAccessorImpl(context, xducer,
prop.getAccessor());
/* */ }
/* 173 */ return new CompositeTransducedAccessorImpl(context,
xducer, prop.getAccessor());
/* */ }
The NPE happens because "xducer" is null.
I googled this class, method, and line number, and I found one person on
StackOverflow who got an NPE on the same line, but his situation seems
to be slightly different.
So, I figure I must have made a mistake in setting up the XmlAdapter.
The following is where I declared the field I'm annotating:
@XmlElement(name = "myMap")
@XmlJavaTypeAdapter(HashMapStringStringAdapter.class)
private HashMap<String, String> myMap;
Here's the adapter class:
public class HashMapStringStringAdapter extends
XmlAdapter<HashMapType<String,String>,HashMap<String,String>> {
@Override
public HashMap<String, String> unmarshal(HashMapType<String, String>
value) throws Exception {
HashMap<String, String> result = new HashMap<String, String>();
for (HashMapEntryType<String, String> hashMapEntry :
value.getEntries())
result.put(hashMapEntry.key, hashMapEntry.value);
return result;
}
@Override
public HashMapType<String, String> marshal(HashMap<String, String>
value) throws Exception {
HashMapType<String, String> result = new HashMapType<String,
String>();
for (Map.Entry<String, String> entry : value.entrySet())
result.put(entry.getKey(), entry.getValue());
return result;
}
}
Here's the HashMapType:
public class HashMapType<K,V> {
private List<HashMapEntryType<K,V>> entries = new
ArrayList<HashMapEntryType<K,V>>();
public List<HashMapEntryType<K,V>> getEntries() { return entries; }
public void setEntries(List<HashMapEntryType<K,V>> entries) {
this.entries = entries; }
public void put(K key, V value) {
getEntries().add(new HashMapEntryType<K,V>(key, value));
}
}
And the HashMapEntryType:
public class HashMapEntryType<K,V> {
@XmlAttribute
public K key;
@XmlValue
public V value;
public HashMapEntryType() {}
public HashMapEntryType(K key, V value) {
this.key = key;
this.value = value;
}
}
Anyone have any idea what I might be doing wrong?