【问题标题】:Spring Boot (2.3.6.RELEASE) Deserialization Fails when using RestTemplate and Unwrap RootSpring Boot (2.3.6.RELEASE) 使用 RestTemplate 和 Unwrap Root 时反序列化失败
【发布时间】:2021-03-17 00:40:58
【问题描述】:

我正在尝试使用 RestTemplate 使用 API,但它根本不会将 json 响应反序列化到我的 pojo 中 这是我试图反序列化的 json 有效负载:

"Response": {
        "Count": 77,
        "Data": [
            {
                "AllowDelete": "1",
                "ContactCount": 1482,
                "CreatedDate": "Dec 01, 2020",
                "ID": "17991951",
                "IsImporting": "0",
                "IsMasterUnsubscribe": "0",
                "ListAudited": "1",
                "ListDescription": "City of Markham Staff - December 2020 (LATEST)",
                "ListImportV3": "1",
                "ListType": "0",
                "ModifiedDate": "Dec 03, 2020",
                "Name": "City of Markham Staff - December 2020 (LATEST)",
                "NameShort": "City of Markham Staff - December 2020 (LATEST)",
                "PermissionPassList": "0",
                "Segments": [],
                "Status": ""
            },{
                "AllowDelete": "0",
                "ContactCount": 884,
                "CreatedDate": "Nov 04, 2011",
                "ID": "582203",
                "IsImporting": "0",
                "IsMasterUnsubscribe": "1",
                "ListAudited": "1",
                "ListDescription": "Master Unsubscribe List",
                "ListImportV3": "0",
                "ListType": "0",
                "ModifiedDate": "Dec 04, 2020",
                "Name": "Master Unsubscribe List",
                "NameShort": "Master Unsubscribe List",
                "PermissionPassList": "0",
                "Segments": [],
                "Status": ""
            }
        ],
        "Status": "1"
    }
}

这是我的主要目标:

package com.markham.enews.model;

import java.util.List;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonRootName;

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonRootName(value = "Response")
public class Contact {

    //Total number
    private int count;

    //1 if successful, -1 if error
    private String status;

    // Further details of the Contact List
    private List<ContactFullRecord> data;

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public List<ContactFullRecord> getData() {
        return data;
    }

    public void setData(List<ContactFullRecord> data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "Contact [count=" + count + ", status=" + status + ", data=" + data + "]";
    }

}

根据这个堆栈溢出链接Spring Boot Jackson with Root name

我在 application.properties 中添加了以下内容:

spring.jackson.mapper.accept-case-insensitive-properties=true
spring.jackson.deserialization.unwrap-root-value=true

我的rest控制器get方法如下:

@GetMapping(value = "/ContactTest")
private Contact getContactTest() {

    String uri = "https://clientapi.benchmarkemail.com/Contact/";
    RestTemplate restTemplate = new RestTemplate();

    HttpEntity<String> request = new HttpEntity<String>(createHeaders());
    ResponseEntity<Contact> response = restTemplate.exchange(uri, HttpMethod.GET, request, Contact.class);

    Contact contact = response.getBody();

    return contact;
}

但生成的对象具有所有空/空值: “计数”:0, “状态”:空, “数据”:空

我认为 unwrap root 和/或不区分大小写的属性没有被拾取.. 如果我编写以下单元测试并直接使用 objectMapper,它可以工作:

    @Test
public void wrapRootValue() throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
    mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
    String str = "{    \"Response\": {\"Count\": 77,\"Data\": [{\"AllowDelete\": \"0\",\"ContactCount\": 884,\"CreatedDate\": \"Nov 04, 2011\",\"ID\": \"582203\",\"IsImporting\": \"0\",\"IsMasterUnsubscribe\": \"1\",\"ListAudited\": \"1\",\"ListDescription\": \"Master Unsubscribe List\",\"ListImportV3\": \"0\",\"ListType\": \"0\",\"ModifiedDate\": \"Dec 03, 2020\",\"Name\": \"Master Unsubscribe List\",\"NameShort\": \"Master Unsubscribe List\",\"PermissionPassList\": \"0\",\"Segments\": [],\"Status\": \"\"}],\"Status\": \"1\"}}";
    Contact root = mapper.readValue(str, Contact.class);
    System.out.println(root);
}

输出:

Contact [count=77, status=1, data=[ContactFullRecord [id=582203, name=Master Unsubscribe List, nameShort=Master Unsubscribe List, status=, contactCount=884.0, createdDate=Nov 04, 2011, modifiedDate=Dec 03, 2020, permissionPassList=0, listAudited=1, listDescription=Master Unsubscribe List, isImporting=0, isMasterUnsubscribe=1, allowDelete=0, listImportV3=0]]]

任何帮助将不胜感激!

【问题讨论】:

  • json 第一个字母应该总是小写。并且 pojo 字段名称的第一个字母也应该是小写的。
  • 嗨@priyranjan 是的,我为我的 pojo 维护了 Java 约定。但是我无法控制 json 有效负载,因为它的第一个字母是大写的。这就是为什么我添加了 jackson 属性:spring.jackson.mapper.accept-case-insensitive-properties=true 但它没有被拾取..
  • 您正在创建一个全新的RestTEmplate,它不使用ObjectMapper 的自动配置。而是通过创建一个 @Bean 方法来注入 RestTEmplate,该方法采用 RestTemplateBuilder 来构造使用预配置对象映射器的其余模板。
  • 谢谢@M.Deinum!

标签: java spring spring-boot jackson


【解决方案1】:

使用 spring boot 预配置 RestTemplateBuilder(已应用所有杰克逊消息转换器配置)并使用 build 请求新的 RestTemplate 实例。

@Configuration
public class RestTemplateConfig {

   @Bean
   public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
      return restTemplateBuilder.build();
   }

}

将实例自动装配到控制器类中。

@Autowired 
private RestTemplate restTemplate;

@GetMapping(value = "/ContactTest")
private Contact getContactTest() {

    String uri = "https://clientapi.benchmarkemail.com/Contact/";

    HttpEntity<String> request = new HttpEntity<String>(createHeaders());
    ResponseEntity<Contact> response = restTemplate.exchange(uri, HttpMethod.GET, request, Contact.class);

    Contact contact = response.getBody();

    return contact;
}

您还可以查看https://www.baeldung.com/spring-rest-template-builder 了解其他设置。

【讨论】:

  • 非常感谢@s7vr。我应该想到创建一个新实例而不是注入忽略了我的配置。
【解决方案2】:

问题是您在 Spring Boot 级别配置 Jackson 反序列化行为,您没有为 RestTemplate 配置反序列化行为。

您可以遵循的一种可能方法是@s7vr 在他/她的回答中建议的方法,并重用 Spring Boot 提供的配置。

如果您只想为您的 RestTemplate 自定义 Jackson 配置,您可以这样做:

final List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();

// Base converters
messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(new StringHttpMessageConverter());
messageConverters.add(new ResourceHttpMessageConverter(false));
messageConverters.add(new SourceHttpMessageConverter<>());
messageConverters.add(new AllEncompassingFormHttpMessageConverter());

// Custom Jackson Converter
final MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
final ObjectMapper mapper = mappingJackson2HttpMessageConverter.getObjectMapper();
mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
messageConverters.add(mappingJackson2HttpMessageConverter);

final RestTemplate restTemplate = new RestTemplate(messageConverters);

// Use it as you consider appropriate

String uri = "https://clientapi.benchmarkemail.com/Contact/";

HttpEntity<String> request = new HttpEntity<String>(createHeaders());
ResponseEntity<Contact> response = restTemplate.exchange(uri, HttpMethod.GET, request, Contact.class);

Contact contact = response.getBody();

//...

当然,如果需要,您可以通过为 RestTemplate 配置 FactoryBean 并稍后在控制器中注入来重用此配置。

【讨论】:

  • 非常感谢@jccampanero。我使用了 s7vr 建议,它获取了我的配置属性。我应该想到创建一个新实例而不是注入它会忽略我的自定义属性。请再问一个问题:我如何 Iog jackson 异常?如果我注释掉忽略大小写属性,它会返回空但不会给我任何错误反馈。再次感谢你!另外,是否也可以接受您的答案?这样你们俩都能得到赏金?
  • 你可以在完成后放置一个新的赏金,但是拜托,这是不必要的。很好,这是一个很好的答案和解决方案。好吧,问题是在很多情况下,从杰克逊的角度来看,并且根据提供的配置,没有错误,它只是找不到属性,......当有错误时,也很难处理。请考虑例如以下可能的方法:stackoverflow.com/questions/51519381/…。请参阅这篇文章:baeldung.com/jackson-exception
  • 如果您有其他需要,请随时与我联系。
猜你喜欢
  • 2018-02-05
  • 2021-03-08
  • 1970-01-01
  • 2017-06-23
  • 2020-09-10
  • 1970-01-01
  • 1970-01-01
  • 2019-04-03
  • 2015-09-01
相关资源
最近更新 更多