Spring Boot (2.3.6.RELEASE) 使用 RestTemplate 和 Unwrap Root 时反序列化失败
Spring Boot (2.3.6.RELEASE) Deserialization Fails when using RestTemplate and Unwrap Root
我正在尝试使用 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"
}
}
这是我的主要 pojo:
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 + "]";
}
}
根据这个堆栈溢出 link Spring Boot Jackson with Root name
我在 application.properties 中添加了以下内容:
spring.jackson.mapper.accept-case-insensitive-properties=true
spring.jackson.deserialization.unwrap-root-value=true
我的rest controller 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;
}
但是生成的对象具有所有 empty/null 值:
“计数”:0,
“状态”:空,
“数据”:空
我认为 unwrap root and/or 不区分大小写的属性没有被拾取..
如果我编写以下单元测试并直接使用 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]]]
如有任何帮助,我们将不胜感激!
使用 spring 引导预配置 RestTemplateBuilder
(已应用所有 jackson 消息转换器配置)并使用 build
请求新的 RestTemplate 实例。
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder.build();
}
}
将实例自动连接到控制器 class。
@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 了解其他设置。
问题是您在 Spring 启动级别配置 Jackson 反序列化行为,而不是为您的 RestTemplate
.
配置反序列化行为
您可以遵循的一种可能方法是@s7vr 在his/her 回答中建议的方法,并重新使用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
并稍后注入您的控制器来重用此配置。
我正在尝试使用 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"
}
}
这是我的主要 pojo:
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 + "]";
}
}
根据这个堆栈溢出 link Spring Boot Jackson with Root name
我在 application.properties 中添加了以下内容:
spring.jackson.mapper.accept-case-insensitive-properties=true
spring.jackson.deserialization.unwrap-root-value=true
我的rest controller 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;
}
但是生成的对象具有所有 empty/null 值: “计数”:0, “状态”:空, “数据”:空
我认为 unwrap root and/or 不区分大小写的属性没有被拾取.. 如果我编写以下单元测试并直接使用 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]]]
如有任何帮助,我们将不胜感激!
使用 spring 引导预配置 RestTemplateBuilder
(已应用所有 jackson 消息转换器配置)并使用 build
请求新的 RestTemplate 实例。
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder.build();
}
}
将实例自动连接到控制器 class。
@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 了解其他设置。
问题是您在 Spring 启动级别配置 Jackson 反序列化行为,而不是为您的 RestTemplate
.
您可以遵循的一种可能方法是@s7vr 在his/her 回答中建议的方法,并重新使用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
并稍后注入您的控制器来重用此配置。