users@jersey.java.net

[Jersey] Unmarshalling of JSON using XmlAdapter with Jersey client

From: Christian Gerteis <gerteisc_at_dhbw-loerrach.de>
Date: Tue, 19 Jul 2011 17:15:38 +0200

Hello,

I'm having trouble unmarshalling some JSON back into the desired type using a XmlAdapter. In particular I'm marshalling a java.util.Map into JSON on the server side. So far it works fine. The unmarshalling on the client side gives me the following stack trace:

Exception in thread "main" javax.ws.rs.WebApplicationException: javax.xml.bind.UnmarshalException
 - with linked exception:
[java.lang.NullPointerException]
    at com.sun.jersey.core.provider.jaxb.AbstractRootElementProvider.readFrom(AbstractRootElementProvider.java:113)
    at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:553)
    at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:506)
    at com.sun.jersey.api.client.WebResource.handle(WebResource.java:674)
    at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
    at com.sun.jersey.api.client.WebResource$Builder.get(WebResource.java:503)
    at test.JerseyClient.main(JerseyClient.java:113)
Caused by: javax.xml.bind.UnmarshalException
 - with linked exception:
[java.lang.NullPointerException]
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.handleStreamException(UnmarshallerImpl.java:414)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:351)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:328)
    at com.sun.jersey.json.impl.BaseJSONUnmarshaller.unmarshalJAXBElementFromJSON(BaseJSONUnmarshaller.java:108)
    at com.sun.jersey.json.impl.BaseJSONUnmarshaller.unmarshalFromJSON(BaseJSONUnmarshaller.java:97)
    at com.sun.jersey.json.impl.provider.entity.JSONRootElementProvider.readFrom(JSONRootElementProvider.java:125)
    at com.sun.jersey.core.provider.jaxb.AbstractRootElementProvider.readFrom(AbstractRootElementProvider.java:111)
    ... 6 more
Caused by: java.lang.NullPointerException
    at org.apache.xml.utils.DOMBuilder.startElement(DOMBuilder.java:322)
    at org.apache.xalan.transformer.TransformerIdentityImpl.startElement(TransformerIdentityImpl.java:1020)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.DomLoader.startElement(DomLoader.java:107)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiTypeLoader.startElement(XsiTypeLoader.java:62)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:470)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:448)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.InterningXmlVisitor.startElement(InterningXmlVisitor.java:60)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.StAXStreamConnector.handleStartElement(StAXStreamConnector.java:231)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.StAXStreamConnector.bridge(StAXStreamConnector.java:165)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:349)
    ... 11 more
Caused by: java.lang.NullPointerException
    at org.apache.xml.utils.DOMBuilder.startElement(DOMBuilder.java:289)
    ... 20 more


The funny thing is, that unmarshalling the data when set to XML works fine. Only JSON seems to cause problems. My Ressource looks like this:

@Path("/")
public class MyResource
{
    @GET
    @Path("/map")
    @Produces(MediaType.APPLICATION_JSON)
    public MapHolder getMap()
    {
        MapHolder model = new MapHolder();
        model.fillMap();
        
        return model;
    }
}

@XmlRootElement(name="map-holder")
@XmlAccessorType(XmlAccessType.NONE)
public class MapHolder
{
    private Map map;
    
    public MapHolder()
    {
        map = new HashMap();
    }
    
    @XmlElement
    @XmlJavaTypeAdapter(MapAdapter.class)
    public Map getMap()
    {
        return map;
    }
    
    public void setMap(Map map)
    {
        this.map = map;
    }
    
    public void fillMap()
    {
        map.put(1234567890l, 0.0);
        map.put(9876543210l, 1.0);
    }
}

This is converted using the following XmlAdapter:

public class MapAdapter extends XmlAdapter,Map>
{
    @Override
    public Map unmarshal(MapBean v) throws Exception
    {
        Map map = new HashMap();
        
        for(MapEntryBean entry : v.getEntry())
        {
            map.put(entry.getKey(), entry.getValue());
        }
        
        return map;
    }

    @Override
    public MapBean marshal(Map v) throws Exception
    {
        return new MapBean(v);
    }
}

@XmlRootElement(name="map")
@XmlAccessorType(XmlAccessType.NONE)
public class MapBean
{
    private List> entry;
    
    public MapBean()
    {
        entry = new ArrayList>();
    }
    
    public MapBean(Map map)
    {
        entry = new ArrayList>();
        
        for(Map.Entry e : map.entrySet())
        {
            entry.add(new MapEntryBean(e));
        }
    }
    
    @XmlElement
    public List> getEntry()
    {
        return entry;
    }

    public void setEntry(List> entry)
    {
        this.entry = entry;
    }
}

@XmlType(name="entry")
@XmlAccessorType(XmlAccessType.NONE)
public class MapEntryBean
{
    private K key;
    private V value;
    
    public MapEntryBean()
    {
        
    }
    
    public MapEntryBean(Map.Entry entry)
    {
        this.key = entry.getKey();
        this.value = entry.getValue();
    }
    
    @XmlElement
    public K getKey()
    {
        return key;
    }
    
    public void setKey(K key)
    {
        this.key = key;
    }
    
    @XmlElement
    public V getValue()
    {
        return value;
    }
    
    public void setValue(V value)
    {
        this.value = value;
    }
}


Calling the desired service inside the browser gives me the following JSON data:

{"map":{"entry":[{"key":{"@type":"xs:long","$":"9876543210"},"value":{"@type":"xs:double","$":"1.0"}},{"key":{"@type":"xs:long","$":"1234567890"},"value":{"@type":"xs:double","$":"0.0"}}]}}


The code for unmarshalling on the client side is:

MapHolder holder = webResource.path("map").accept(MediaType.APPLICATION_JSON).get(MapHolder.class);
System.out.println(holder.getMap().entrySet());


I'm probably missing something obvious but I have no clue what is causing the problem. That's why I included the whole test source code, as I don't know where things go wrong.

Any help is highly appreciated.


Thanks in advice
Chris


--
Christian Gerteis
TIT 08