休息模板 |读取未知的 ENUM 值作为默认值

RestTemplate | Reading unknown ENUM values as default value

我正在使用 org.springframework:spring-web:5.1.9.RELEASE 并且有一个普通的(未定制的)RestTemplate class 对象,我用它来发送 POST 使用 EXCHANGE 方法请求。我想让它反序列化来自 response 的未知 ENUM 值到它们的默认值(使用 @JsonEnumDefaultValue 设置)而不是失败整个操作。我已经搜索过,但没有找到任何简单的方法来做到这一点,你能给我一些帮助吗?谢谢!

@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);
    }
}

这是我现在遇到的异常:

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]

类:

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;
        }
    }
}

您可能需要为您正在使用的 RestTemplate 配置自定义 ObjectMapper。您需要在 ObjectMapper 上启用 this feature请确保您使用的是 fasterxml 作为所有这些的包

为了配置一个,像这样创建一个 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();

    // This where you enable default enum feature
    objectMapper.configure(DeserializationFeature. READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, true);

    return objectMapper;
}

我能够使用放置在 ENUM class 中的 JsonCreator 解决它。

@JsonCreator
    public static Type getByValue(String t) {
        return Arrays.stream(Type.values())
                .filter(a -> a.name().equals(t)).findFirst().orElse(Type.UNKNOWN);
    }
}