23

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.

JJD
  • 50,076
  • 60
  • 203
  • 339
Gabriela Radu
  • 757
  • 2
  • 12
  • 33
  • 1
    What is the json structure, how MyObject looks. Not enough information. The only guess I see is that you're getting array from server and trying to map it to MyObject. – pawelzieba May 23 '12 at 12:34
  • in the client: `public class Consultant { private int id; private String firstName; private String lastName; private String phoneNumber; private String jobName; private String workAddress; private Date created; private String email; getters and setters } ` – Gabriela Radu May 23 '12 at 12:43
  • and the response json is this: `[{"id":1,"firstName":"first name","lastName":"last name","jobName":"programmer","created":1328133600000,"email":"test@yahoo.com","workAddress":"test","phoneNumber":"0000"},{...},{..}]` – Gabriela Radu May 23 '12 at 12:46
  • 1
    Yes, then you should map it to List rather than to Consultant.class. – pawelzieba May 23 '12 at 13:09
  • You are correct. Thank you. If you don't write the last comment as an answer I cannot tag your response as the correct one :D – Gabriela Radu May 23 '12 at 14:20

5 Answers5

23

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

pawelzieba
  • 16,082
  • 3
  • 46
  • 72
18

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.

Ahto Luuri
  • 238
  • 3
  • 12
9

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)
);
JJD
  • 50,076
  • 60
  • 203
  • 339
NathanChristie
  • 2,374
  • 21
  • 20
0

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);
Tiina
  • 4,285
  • 7
  • 44
  • 73
0

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.

WesternGun
  • 11,303
  • 6
  • 88
  • 157