Gabriela Radu
Gabriela Radu

Reputation: 767

Cannot deserialize instance of object out of START_ARRAY token in Spring 3 REST Webservice

I am making use of this cool thing Spring offers: Spring RESTWebService (Version of spring is 3). If I access the URL from browser I can see the JSON response, but from a Client endpoint (Android application) iIreceive this error message:

Caused by: org.springframework.web.client.ResourceAccessException: 
    I/O error: Can not deserialize instance of MyObject out of START_ARRAY token
  at [Source: org.apache.http.conn.EofSensorInputStream@4076e940; line: 1, 
    column: 1]; nested exception is org.codehaus.jackson.map.JsonMappingException: 
    Can not deserialize instance of MyObject  out of START_ARRAY token
  at [Source: org.apache.http.conn.EofSensorInputStream@4076e940; line: 1, column: 1]
   at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:466)
   at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:414)
   at org.springframework.web.client.RestTemplate.getForEntity(RestTemplate.java:234)
   at com.be.android.locateconsultants.resources.AsyncTaskRESTServiceCaller.doInBackground(AsyncTaskRESTServiceCaller.java:43)
   at com.be.android.locateconsultants.resources.AsyncTaskRESTServiceCaller.doInBackground(AsyncTaskRESTServiceCaller.java:1)
   at android.os.AsyncTask$2.call(AsyncTask.java:252)
   at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
   ... 4 more

 Caused by: org.codehaus.jackson.map.JsonMappingException: Can not deserialize 
    instance of MyObject  out of START_ARRAY token
  at [Source: org.apache.http.conn.EofSensorInputStream@4076e940; line: 1, column: 1]
   at org.codehaus.jackson.map.deser.StdDeserializationContext.mappingException(StdDeserializationContext.java:198)
   at org.codehaus.jackson.map.deser.BeanDeserializer.deserializeUsingCreator(BeanDeserializer.java:565)
   at org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.java:365)
   at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:2395)
   at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1655)
   at org.springframework.http.converter.json.MappingJacksonHttpMessageConverter.readInternal(MappingJacksonHttpMessageConverter.java:135)
   at org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.java:154)
   at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:74)
   at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:632)
   at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:618)
   at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:459)
   ... 10 more

MyObject structure is the same as the one from the server side application.

I have tried to request the server like this:

final String url = ".....";
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<Consultant> responseEntity = restTemplate.getForEntity(
            url, Consultant.class);

Or like this:

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<String>(headers);
ResponseEntity<MyObject> response = restTemplate
            .exchange("....",HttpMethod.GET, entity, MyObject.class);
System.out.println("RESPONSE: " + response.getBody());

But still the same error as above. Can't figure out what I am missing at this point, any idea or hints would be great. Thank you.

Upvotes: 23

Views: 99709

Answers (5)

WesternGun
WesternGun

Reputation: 12797

If you want to serialize an array from a json like:

[
  {"foo":"bar"}, {"foo1":"bar1"}
]

You must configure the value deserializer to accept an array in the method signature.

    @Bean
    public ConsumerFactory<String, DTO[]> consumerFactory() {
        Map<String, Object> props = new HashMap<>();
        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,
                bootstrapAddress);
        props.put(ConsumerConfig.GROUP_ID_CONFIG,
                groupId);
        props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest");
        props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, true);
        props.put(ConsumerConfig.ALLOW_AUTO_CREATE_TOPICS_CONFIG, true);
        return new DefaultKafkaConsumerFactory<>(props,
                new StringDeserializer(),
                new JsonDeserializer<>(DTO[].class));
    }

    @Bean
    public ConcurrentKafkaListenerContainerFactory<String, DTO[]>
    kafkaListenerContainerFactory() {
        ConcurrentKafkaListenerContainerFactory<String, DTO[]> factory =
                new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(consumerFactory());
        return factory;
    }

And in your consumer:

@KafkaListener(topics = "foo-topic", groupId = "foo-group")
void listen(DTO[] dtos) {

}

No type reference or other things needed. I think underneath, ObjectArrayDeserializer is used.

Note that in this way, you will need to put all configs into java code, and (maybe) all configs by YAML will not work for consumer. I am not sure but you can try to combine code config and YAML config.

Upvotes: 0

Tiina
Tiina

Reputation: 4797

a file foo.json contains a batch of data like this:

[{"A":"TYMH","B":"datab","C":"DLJQ","D":"datad"}, {"A":"TYMH","B":"datab","C":"DLJQ","D":"datad"}]

The following statement gives you an ArrayList of LinkedHashMap, where A B C D are the keys.

List list = mapper.readValue(new File("D:\\...\\foo.json"), List.class);

Upvotes: 0

NathanChristie
NathanChristie

Reputation: 2400

This is related to Jackson and the way you're attempting to deserialize and initialize a container from an array.

While my usage context is a bit different, this may help some who get here from searching for Jackson-specific deserialization errors.

I had to do it like this:

List<Consultant> consultants = Arrays.asList(
    mapper.readValue(myJson.toString(), Consultants[].class)
);

Upvotes: 9

Ahto Luuri
Ahto Luuri

Reputation: 238

Solution in a similar situation was to map to Consultant[].class This applies if you try to deserialize a JSON array of your mapped objects.

Upvotes: 18

pawelzieba
pawelzieba

Reputation: 16082

You should map it to List<Consultant> rather than to Consultant.class.

Upvotes: 23

Related Questions