srini
srini

Reputation: 213

Jersey client read json response into custom object

public class RESTDataServiceClient{    
    private Client client;
    private String dataServiceUri;
    private String dataServiceResource;    
    private CustomData customData;

    public RESTDataServiceClient(String dataServiceUri, String dataServiceResource, Client client){
        this.client = client;
        this.dataServiceUri = dataServiceUri;
        this.dataServiceResource = dataServiceResource;
    }

    @Override
    public CustomData getCustomData() {
        WebTarget dataServiceTarget = client.target(dataServiceUri).path(dataServiceResource);
        Invocation.Builder invocationBuilder = dataServiceTarget.request(MediaType.APPLICATION_JSON_TYPE);
        Response response = invocationBuilder.get();
        myCustomData = response.readEntity(CustomData.class);
        return myCustomData;
    }
}

CustomData.java
public class CustomData{
        private TLongObjectMap<Map<String, TIntIntMap>> data;
        public CustomData() {
            this.data = new TLongObjectHashMap<>();
    }
    //getter and setter
}

sample json content

 {"50000":{"testString":{"1":10}},"50001":{"testString1":{"2":11}} }

I am trying to get data from a data service which is going to return data in a JSON format. I am trying to write a client to read that JSON into a custom object. The CustomData contains a nested trove map datastructure. we wrote a custom serializer for that and the server part works fine. I am unable to get the rest client read the data into an object, but reading into string works. I tried above pasted code with the sample data and i get the error below.

javax.ws.rs.ProcessingException: Error reading entity from input stream.
    at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:866)
    at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:783)
    at org.glassfish.jersey.client.ClientResponse.readEntity(ClientResponse.java:326)
    at org.glassfish.jersey.client.InboundJaxrsResponse$1.call(InboundJaxrsResponse.java:111)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:399)
    at org.glassfish.jersey.client.InboundJaxrsResponse.readEntity(InboundJaxrsResponse.java:108)
    at com.sample.data.RESTDataServiceClient.getCustomData(RESTDataServiceClient.java:42)

Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "50000" (class com.sample.data.CustomData), not marked as ignorable (0 known properties: ])
 at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream@2cb89281; line: 1, column: 14] (through reference chain: com.sample.data.CustomData["50000"])
    at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:51)
    at com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty(DeserializationContext.java:671)
    at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:773)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1297)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1275)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:247)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:118)
    at com.fasterxml.jackson.databind.ObjectReader._bind(ObjectReader.java:1233)
    at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:677)
    at com.fasterxml.jackson.jaxrs.base.ProviderBase.readFrom(ProviderBase.java:777)
    at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.invokeReadFrom(ReaderInterceptorExecutor.java:264)
    at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:234)
    at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:154)
    at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1124)
    at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:851)
    ... 38 more

Upvotes: 1

Views: 4915

Answers (1)

Sam Berry
Sam Berry

Reputation: 7844

TLongObjectMap is not deserializable out of the box, so how you made a custom serializer you also need to implement a custom deserializer. You can package these up nicely in a module and add it to your ObjectMapper.

It looks like there is a Trove module in development right now, which you can download and add to your ObjectMapper the same as the example below. The TIntObjectMapDeserializer implementation in that link is much more robust then my solution, so I would recommend using that class in your project if possible.

If you want to try and write it yourself, here's a starting point that properly deserializes your provided example:

public class FakeTest {
    @Test
    public void test() throws Exception {
        ObjectMapper om = new ObjectMapper();
        om.registerModule(new CustomModule());
        String s = "{\"50000\":{\"testString\":{\"1\":10}},\"50001\":{\"testString1\":{\"2\":11}} }";
        CustomData cd = om.readValue(s, CustomData.class);
        System.out.println(cd.getData());
    }

    public static class CustomData {
        private TLongObjectMap<Map<String, TIntIntMap>> data;

        public CustomData() {
            this.data = new TLongObjectHashMap<>();
        }

        public TLongObjectMap<Map<String, TIntIntMap>> getData() { return data; }

        public void setData(TLongObjectMap<Map<String, TIntIntMap>> data) { this.data = data; }
    }

    public static class CustomModule extends SimpleModule {
        public CustomModule() {
            addSerializer(CustomData.class, new CustomSerializer());
            addDeserializer(CustomData.class, new CustomDeserializer());
        }

        public static class CustomSerializer extends JsonSerializer<CustomData> {
            @Override
            public void serialize(CustomData value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
                // add custom serializer here
            }
        }

        public static class CustomDeserializer extends JsonDeserializer<CustomData> {
            @Override
            public CustomData deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
                TLongObjectMap<Map<String, TIntIntMap>> data = new TLongObjectHashMap<>();
                ObjectNode node = jsonParser.getCodec().readTree(jsonParser);
                Iterator<Map.Entry<String,JsonNode>> fields = node.fields();
                while (fields.hasNext()) {
                    Map.Entry<String, JsonNode> entry = fields.next();
                    ObjectNode value = (ObjectNode) entry.getValue();
                    Map.Entry<String, JsonNode> innerField = value.fields().next();
                    ObjectNode innerNode = (ObjectNode) innerField.getValue();
                    Map.Entry<String, JsonNode> innerInnerField = innerNode.fields().next();
                    TIntIntMap intMap = new TIntIntHashMap();
                    intMap.put(Integer.parseInt(innerInnerField.getKey()), innerInnerField.getValue().asInt());
                    Map<String, TIntIntMap> innerMap = Collections.singletonMap(innerField.getKey(), intMap);
                    data.put(Long.parseLong(entry.getKey()), innerMap);
                }
                CustomData customData = new CustomData();
                customData.setData(data);
                return customData;
            }
        }
    }
}

Upvotes: 1

Related Questions