Deserialising JSON array using RestTemplate



I’m trying to convert JSON data to a java class using Rest Template.

The JSON data has this format and cannot be changed:

{
   "items":[
      {
         "id":"5818",
         "seconds_since_report":21,
         "latitude":34.0954089,
         "run_id":"78_411_1",
         "predictable":true,
         "route_id":"78",
         "heading":271.0,
         "longitude":-118.152489
      },
      {
         "id":"5819",
         "seconds_since_report":48,
         "latitude":33.9197757,
         "run_id":"260_267_1",
         "predictable":true,
         "route_id":"260",
         "heading":199.0,
         "longitude":-118.1880995
      },
      {
         "id":"5813",
         "seconds_since_report":21,
         "latitude":34.134002,
         "run_id":"181_191_0",
         "predictable":true,
         "route_id":"181",
         "heading":101.0,
         "longitude":-118.209497
      }
   ]
}

The java class to save the data is:

public class Items {
    private Collection<Vehicle> items;
    public Collection<Vehicle> getItems() {
        return items;
    }
    public void setItems(Collection<Vehicle> items) {
        this.items = items;
    }   
}
import com.fasterxml.jackson.annotation.JsonFormat;

@JsonFormat(shape=JsonFormat.Shape.ARRAY)
public class Vehicle {
    private String id;
    private String run_id; 
    private String route_id; 
    private Double latitude; 
    private Double longitude;
    private boolean predictable;
    private int seconds_since_report;
    private Double heading;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getRun_id() {
        return run_id;
    }
    public void setRun_id(String run_id) {
        this.run_id = run_id;
    }
    public String getRoute_id() {
        return route_id;
    }
    public void setRoute_id(String route_id) {
        this.route_id = route_id;
    }
    public Double getLatitude() {
        return latitude;
    }
    public void setLatitude(Double latitude) {
        this.latitude = latitude;
    }
    public Double getLongitude() {
        return longitude;
    }
    public void setLongitude(Double longitude) {
        this.longitude = longitude;
    }
    public boolean isPredictable() {
        return predictable;
    }
    public void setPredictable(boolean predictable) {
        this.predictable = predictable;
    }
    public int getSeconds_since_report() {
        return seconds_since_report;
    }
    public void setSeconds_since_report(int seconds_since_report) {
        this.seconds_since_report = seconds_since_report;
    }
    public Double getHeading() {
        return heading;
    }
    public void setHeading(Double heading) {
        this.heading = heading;
    }   
}

To receive the JSON data and deserialize i’m doing:

RestTemplate restTemplate = new RestTemplate();
ResponseEntity<Items> data = restTemplate.getForEntity(url, Items.class);

But i’m getting this exception:

org.springframework.web.client.RestClientException: Error while extracting response for type [class com.es.projectbackend.model.Items] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize a POJO (of type com.es.projectbackend.model.Vehicle) from non-Array representation (token: START_OBJECT): type/property designed to be serialized as JSON Array; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize a POJO (of type com.es.projectbackend.model.Vehicle) from non-Array representation (token: START_OBJECT): type/property designed to be serialized as JSON Array
 at [Source: (PushbackInputStream); line: 1, column: 12] (through reference chain: com.es.projectbackend.model.Items["items"]->java.util.ArrayList[0])
    at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:120) ~[spring-web-5.3.5.jar:5.3.5]
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1037) ~[spring-web-5.3.5.jar:5.3.5]
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1020) ~[spring-web-5.3.5.jar:5.3.5]
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:778) ~[spring-web-5.3.5.jar:5.3.5]
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:711) ~[spring-web-5.3.5.jar:5.3.5]
    at org.springframework.web.client.RestTemplate.getForEntity(RestTemplate.java:361) ~[spring-web-5.3.5.jar:5.3.5]
    at com.es.projectbackend.service.LAMetroService.updateLAMetroVehicles(LAMetroService.java:57) ~[classes/:?]
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64) ~[?:?]
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
    at java.lang.reflect.Method.invoke(Method.java:564) ~[?:?]
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84) ~[spring-context-5.3.5.jar:5.3.5]
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) [spring-context-5.3.5.jar:5.3.5]
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) [?:?]
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305) [?:?]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) [?:?]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130) [?:?]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630) [?:?]
    at java.lang.Thread.run(Thread.java:832) [?:?]
Caused by: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize a POJO (of type com.es.projectbackend.model.Vehicle) from non-Array representation (token: START_OBJECT): type/property designed to be serialized as JSON Array; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize a POJO (of type com.es.projectbackend.model.Vehicle) from non-Array representation (token: START_OBJECT): type/property designed to be serialized as JSON Array
 at [Source: (PushbackInputStream); line: 1, column: 12] (through reference chain: com.es.projectbackend.model.Items["items"]->java.util.ArrayList[0])
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:389) ~[spring-web-5.3.5.jar:5.3.5]
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:342) ~[spring-web-5.3.5.jar:5.3.5]
    at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:105) ~[spring-web-5.3.5.jar:5.3.5]
    ... 18 more
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize a POJO (of type com.es.projectbackend.model.Vehicle) from non-Array representation (token: START_OBJECT): type/property designed to be serialized as JSON Array
 at [Source: (PushbackInputStream); line: 1, column: 12] (through reference chain: com.es.projectbackend.model.Items["items"]->java.util.ArrayList[0])
    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1468) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1242) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.deser.impl.BeanAsArrayDeserializer._deserializeFromNonArray(BeanAsArrayDeserializer.java:373) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.deser.impl.BeanAsArrayDeserializer.deserialize(BeanAsArrayDeserializer.java:103) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:290) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:249) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:26) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:371) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:164) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4526) ~[jackson-databind-2.11.4.jar:2.11.4]
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3521) ~[jackson-databind-2.11.4.jar:2.11.4]
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:378) ~[spring-web-5.3.5.jar:5.3.5]
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:342) ~[spring-web-5.3.5.jar:5.3.5]
    at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:105) ~[spring-web-5.3.5.jar:5.3.5]
    ... 18 more

I’ve done this before and it worked, but the JSON data had a slightly different format. But this time I cannot understand this error.

Answer

The annotation @JsonFormat(shape=JsonFormat.Shape.ARRAY) lets Jackson look for an array representation of a Vehicle class. That could look like this:

{
   "items":[
      [
        "5818",
        21,
        34.0954089,
        "78_411_1",
        true,
        "78",
        271.0,
        -118.152489
      ]
   ]
}

The order of the variables need to match the order of the class attributes. That’s what the Shape.ARRAY is doing.

What you probably want is the actual Object representation. Therefore just remove the @JsonFormat(shape=JsonFormat.Shape.ARRAY) and it will work.



Source: stackoverflow