英文:
RestTemplate | Reading unknown ENUM values as default value
问题
@Service
@CommonsLog
public class TMServerExternalApiRepositoryImpl implements TMServerExternalApiRepository {
private final RestTemplate restRelay;
private TeleMessageProperties tmUrls;
@Autowired
public TMServerExternalApiRepositoryImpl(RestTemplate restTemplate, TeleMessageProperties tmProperties) {
this.restRelay = restTemplate;
this.tmUrls = tmProperties;
}
@Override
public VnvUsersSearchResult getUsersByPhone(String to, String from) {
log.info(String.format("Trying to get users with telephones to: [%s], from: [%s] ", to, from));
return sendRequest(new VnvUserSearchParams(from, to), VnvUsersSearchResult.class, tmUrls.getGetUserByPhoneUrl()).getBody();
}
//the actual post
private <T, K> ResponseEntity<T> sendRequest(K content, Class<T> returnTypeClass, String url) throws HttpClientErrorException {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
HttpEntity<K> httpEntity = new HttpEntity<>(content, httpHeaders);
return restRelay.exchange(url, HttpMethod.POST, httpEntity, returnTypeClass);
}
}
public class VnvUsersSearchResult {
private VNVUser userFrom;
// ...
}
public class VNVUser {
// ...
protected List<VNVDevice> devices;
// ...
}
public class VNVDevice {
public Type type;
String mobile;
public enum Type {
@JsonEnumDefaultValue
UNKNOWN(0),
MOBILE(10),
BUSINESS_PHONE(20),
// ...
int ID;
Type(int i) {
this.ID = i;
}
}
}
Exception:
org.springframework.web.client.RestClientException: Error while extracting response for type [class voiceAndVideo.services.VnvUsersSearchResult] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `voiceAndVideo.Util.VNVDevice$Type` from String "BAD_VALUE": value not one of declared Enum instance names: [MOBILE, ...]; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `voiceAndVideo.Util.VNVDevice$Type` from String "BAD_VALUE": value not one of declared Enum instance names: [MOBILE, ...]
at [Source: (ByteArrayInputStream); line: 1, column: 261] (through reference chain: voiceAndVideo.services.VnvUsersSearchResult["userFrom"]->voiceAndVideo.Util.VNVUser["devices"]->java.util.ArrayList[2]->voiceAndVideo.Util.VNVDevice["type"])
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:117) ~[spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
英文:
I'm using org.springframework:spring-web:5.1.9.RELEASE
and have a plain (not customized) RestTemplate
class object I use to send a POST request by utilizing the EXCHANGE method. I want to allow it desirialize unknown ENUM values from the response to their default value (as set using @JsonEnumDefaultValue
) instead of failing the whole operation. I've searched and didn't find any easy way to do it, can you give me some help with that? Thanks!
@Service
@CommonsLog
public class TMServerExternalApiRepositoryImpl implements TMServerExternalApiRepository {
private final RestTemplate restRelay;
private TeleMessageProperties tmUrls;
@Autowired
public TMServerExternalApiRepositoryImpl(RestTemplate restTemplate, TeleMessageProperties tmProperties) {
this.restRelay = restTemplate;
this.tmUrls = tmProperties;
}
@Override
public VnvUsersSearchResult getUsersByPhone(String to, String from) {
log.info(String.format("Trying to get users with telephones to: [%s], from: [%s] ", to, from));
return sendRequest(new VnvUserSearchParams(from, to), VnvUsersSearchResult.class, tmUrls.getGetUserByPhoneUrl()).getBody();
}
//the actual post
private <T, K> ResponseEntity<T> sendRequest(K content, Class<T> returnTypeClass, String url) throws HttpClientErrorException {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
HttpEntity<K> httpEntity = new HttpEntity<>(content, httpHeaders);
return restRelay.exchange(url, HttpMethod.POST, httpEntity, returnTypeClass);
}
}
This is the exception I am getting now:
org.springframework.web.client.RestClientException: Error while extracting response for type [class voiceAndVideo.services.VnvUsersSearchResult] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `voiceAndVideo.Util.VNVDevice$Type` from String "BAD_VALUE": value not one of declared Enum instance names: [MOBILE, ...]; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `voiceAndVideo.Util.VNVDevice$Type` from String "BAD_VALUE": value not one of declared Enum instance names: [MOBILE, ...]
at [Source: (ByteArrayInputStream); line: 1, column: 261] (through reference chain: voiceAndVideo.services.VnvUsersSearchResult["userFrom"]->voiceAndVideo.Util.VNVUser["devices"]->java.util.ArrayList[2]->voiceAndVideo.Util.VNVDevice["type"])
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:117) ~[spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
Classes:
public class VnvUsersSearchResult {
private VNVUser userFrom;
...
}
public class VNVUser {
...
protected List<VNVDevice> devices;
...
}
public class VNVDevice {
public Type type;
String mobile;
public enum Type {
@JsonEnumDefaultValue
UNKNOWN(0),
MOBILE(10),
BUSINESS_PHONE(20),
...
int ID;
Type(int i) {
this.ID = i;
}
}
}
答案1
得分: 2
你可能需要配置一个自定义的 ObjectMapper
到你正在使用的 RestTemplate
中。你需要在 ObjectMapper
上启用 此功能。请确保你使用的是 fasterxml 作为所有这些包的包名。
为了配置一个自定义的 ObjectMapper
,可以创建一个类似这样的 JavaConfig 文件:
@Bean
public RestOperations restOperations() {
RestTemplate rest = new RestTemplate();
rest.getMessageConverters().add(0, mappingJacksonHttpMessageConverter());
return rest;
}
@Bean
public MappingJacksonHttpMessageConverter mappingJacksonHttpMessageConverter() {
MappingJacksonHttpMessageConverter converter = new MappingJacksonHttpMessageConverter();
converter.setObjectMapper(myObjectMapper());
return converter;
}
@Bean
public ObjectMapper myObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
// 这里是启用默认枚举功能的地方
mapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, true);
return mapper;
}
请按照这些步骤配置你的 RestTemplate
和 ObjectMapper
。
英文:
You probably need to configure a custom ObjectMapper
to the RestTemplate
that you are using. You need to enable this feature on the ObjectMapper
. Please make sure you are using fasterxml as the package for all these.
In order to configure one, create a JavaConfig file like this:
@Bean
public RestOperations restOperations() {
RestTemplate rest = new RestTemplate();
rest.getMessageConverters().add(0, mappingJacksonHttpMessageConverter());
return rest;
}
@Bean
public MappingJacksonHttpMessageConverter mappingJacksonHttpMessageConverter() {
MappingJacksonHttpMessageConverter converter = new MappingJacksonHttpMessageConverter();
converter.setObjectMapper(myObjectMapper());
return converter;
}
@Bean
public ObjectMapper myObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
// This where you enable default enum feature
objectMapper.configure(DeserializationFeature. READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, true);
return objectMapper;
}
答案2
得分: 0
我能够通过在ENUM类中使用JsonCreator来解决它。
@JsonCreator
public static Type getByValue(String t) {
return Arrays.stream(Type.values())
.filter(a -> a.name().equals(t)).findFirst().orElse(Type.UNKNOWN);
}
英文:
I was able to solve it using JsonCreator placed in the ENUM class.
@JsonCreator
public static Type getByValue(String t) {
return Arrays.stream(Type.values())
.filter(a -> a.name().equals(t)).findFirst().orElse(Type.UNKNOWN);
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论