Spring restTemplate 中的 START_ARRAY 令牌

Posted

技术标签:

【中文标题】Spring restTemplate 中的 START_ARRAY 令牌【英文标题】:out of START_ARRAY token in Spring restTemplate 【发布时间】:2020-12-06 09:38:34 【问题描述】:

我有一个 SpringBoot 应用程序。使用这个配置文件:

@Configuration
public class ApplicationConfig 

    @Bean
    public RestTemplate restTemplate() 
        RestTemplate restTemplate = new RestTemplate();
        MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
        mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON, MediaType.APPLICATION_OCTET_STREAM));
        restTemplate.getMessageConverters().add(mappingJackson2HttpMessageConverter);
        return restTemplate;
    


还有这个类:

@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(NON_NULL)
public class GeolocationAddress 

    private Integer placeId;
    private String licence;
    private String osmType;
    private Integer osmId;
    private List<String> boundingbox = null;
    private String lat;
    private String lon;
    private String displayName;
    private String _class;
    private String type;
    private Double importance;
    private Address address;

还有这个:

@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(NON_NULL)
public class GeolocationAddressList 

    private List<GeolocationAddress> addresses;


还有这项服务:

public List<GeolocationAddress> searchFromAddress(String address) 

    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
    HttpEntity<String> entity = new HttpEntity<String>(headers);

    String url = "https://nominatim.openstreetmap.org/?addressdetails=1&q=bakery+in+berlin+wedding&format=json&limit=1";


    GeolocationAddressList geolocationAddressList
            = restTemplate.exchange(url, HttpMethod.GET, entity, GeolocationAddressList.class).getBody();

    return geolocationAddressList.getAddresses();

但我在运行服务时出现此错误:

org.springframework.web.client.RestClientException: Error while extracting response for type [class com.bonansa.domain.GeolocationAddressList] and content type [application/json;charset=UTF-8]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of `com.bonansa.domain.GeolocationAddressList` out of START_ARRAY token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `com.bonansa.domain.GeolocationAddressList` out of START_ARRAY token
 at [Source: (PushbackInputStream); line: 1, column: 1]

JSON

["place_id":80416066,"licence":"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright","osm_type":"way","osm_id":4308033,"boundingbox":["40.4249225","40.4254227","-3.7133155","-3.7124476"],"lat":"40.42515745","lon":"-3.7128816738834214","display_name":"Plaza de Emilio Jiménez Millas, Argüelles, Moncloa-Aravaca, Madrid, Área metropolitana de Madrid y Corredor del Henares, Comunidad de Madrid, 28001, Comunidad de Madrid","class":"highway","type":"pedestrian","importance":0.42816097156854616,"address":"road":"Plaza de Emilio Jiménez Millas","quarter":"Argüelles","city_district":"Moncloa-Aravaca","city":"Madrid","municipality":"Área metropolitana de Madrid y Corredor del Henares","state":"Comunidad de Madrid","postcode":"28001","country_code":"es","administrative":"Comunidad de Madrid"]

【问题讨论】:

某些属性在 JSON 中包含_,因此最好在属性级别上使用@JsonProperty("display_name") 你能在这里发布正确的 json 响应吗?您是否在 postman/soapui 中点击了请求并将响应与您的 POJO 类进行了比较? 响应是一个数组,而你只解析到GeolocationAddress.classlimit=1 没有给你一个条目,而是一个包含一个元素的数组。此外,某些属性包含 _,而您在 GeolocationAddress 中的字段不包含。 【参考方案1】:

使用ParameterizedTypeReference 直接映射响应 到List&lt;GeolocationAddress&gt; 不需要容器对象。

public List<GeolocationAddress> searchFromAddress(String address) 

  HttpHeaders headers = new HttpHeaders();
  headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
  HttpEntity<String> entity = new HttpEntity<String>(headers);

  String url = "https://nominatim.openstreetmap.org/?addressdetails=1&q=bakery+in+berlin+wedding&format=json&limit=1";

  ParameterizedTypeReference<List<GeolocationAddress>> typeRef = new ParameterizedTypeReference<List<GeolocationAddress>();
  List<GeolocationAddress> addresses = restTemplate.exchange(url, HttpMethod.GET, entity, typeRef).getBody();

  return addresses;

【讨论】:

【参考方案2】:

您面临的问题与您请求信息的方式有关。

您正在向服务执行GET 请求并尝试将响应映射为GeolocationAddressList 类的实例。

如果服务实际上返回了一个对象,该对象的属性为 addresses,对应于 GeolocationAddress 的数组,则您的代码将起作用。

但该服务实际上返回了 GeolocationAddress 本身的数组,Jackson 对此表示抱怨 - 正如您表示您正在等待一个对象但该服务实际上正在返回一个数组。

请将您的代码更改为以下内容:

GeolocationAddress[] addresses
        = restTemplate.exchange(url, HttpMethod.GET, entity, GeolocationAddress[].class).getBody();

我认为应该可以。

【讨论】:

【参考方案3】:

把你的电话改成

GeolocationAddress[] geolocationAddresses
        = restTemplate.exchange(url, HttpMethod.GET, entity, GeolocationAddress[].class).getBody();

这应该可行..

您可以直接将您的数组传递给:

java.util.Arrays#asList

然后给你一个List&lt;GeolocationAddress&gt;

另一个好处是您可以删除 GeolocationAddressList,因为它不再需要了。

【讨论】:

以上是关于Spring restTemplate 中的 START_ARRAY 令牌的主要内容,如果未能解决你的问题,请参考以下文章

RestTemplate Spring 启动中的 NullPointerException MediaType

Spring restTemplate 中的 START_ARRAY 令牌

正文中的 Spring Boot OAuth2RestTemplate 客户端凭据

笔记:Spring Cloud Ribbon RestTemplate 详解

RestTemplate Module|休息模板模块

Spring中RestTemplate的使用方法