RestTemplate | 当作默认值阅读未知的枚举值

huangapple go评论78阅读模式
英文:

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(&quot;Trying to get users with telephones to: [%s], from: [%s] &quot;, to, from));
        return sendRequest(new VnvUserSearchParams(from, to), VnvUsersSearchResult.class, tmUrls.getGetUserByPhoneUrl()).getBody();
    }

    //the actual post
    private &lt;T, K&gt; ResponseEntity&lt;T&gt; sendRequest(K content, Class&lt;T&gt; returnTypeClass, String url) throws HttpClientErrorException {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
        HttpEntity&lt;K&gt; httpEntity = new HttpEntity&lt;&gt;(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 &quot;BAD_VALUE&quot;: 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 &quot;BAD_VALUE&quot;: value not one of declared Enum instance names: [MOBILE, ...]
 at [Source: (ByteArrayInputStream); line: 1, column: 261] (through reference chain: voiceAndVideo.services.VnvUsersSearchResult[&quot;userFrom&quot;]-&gt;voiceAndVideo.Util.VNVUser[&quot;devices&quot;]-&gt;java.util.ArrayList[2]-&gt;voiceAndVideo.Util.VNVDevice[&quot;type&quot;])
	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&lt;VNVDevice&gt; 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;
}

请按照这些步骤配置你的 RestTemplateObjectMapper

英文:

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 -&gt; a.name().equals(t)).findFirst().orElse(Type.UNKNOWN);
    }
}

huangapple
  • 本文由 发表于 2020年9月13日 22:01:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/63871657.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定