使用 RestTemplate 反序列化 JSON 数组

Deserialising JSON array using RestTemplate

我正在尝试使用 Rest 模板将 JSON 数据转换为 java class。

JSON数据的格式是这样的,不能更改:

{
   "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
      }
   ]
}

保存数据的javaclass为:

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;
    }   
}

要接收 JSON 数据并反序列化我正在做的事情:

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

但是我遇到了这个异常:

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

我以前做过这个并且成功了,但是 JSON 数据的格式略有不同。但是这次我无法理解这个错误。

首先,移除@JsonFormat(shape=JsonFormat.Shape.ARRAY) 其次,将默认的空构造函数添加到 类

注解 @JsonFormat(shape=JsonFormat.Shape.ARRAY) 让 Jackson 寻找 Vehicle class 的数组表示。 可能看起来像这样:

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

变量的顺序需要匹配class属性的顺序。这就是 Shape.ARRAY 正在做的事情。

您可能想要的是实际的对象表示。因此,只需删除 @JsonFormat(shape=JsonFormat.Shape.ARRAY) 即可。