【问题标题】:InvalidFormatException for Date - fixing without using JsonFormat or modifying original classInvalidFormatException for Date - 在不使用 JsonFormat 或修改原始类的情况下进行修复
【发布时间】:2020-02-13 05:52:26
【问题描述】:

简介

我们正在使用一个托管在 nexus 存储库上的自定义启动器,其中包含向微服务发出请求的 spring-cloud-feign 客户端。

其中一个微服务将日期返回为“dd-MM-yyyy HH:mm:ssZ”,这适用于我们的大多数应用程序。但是,我们有一个应用程序抛出以下错误:

Caused by: com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.util.Date` from String "2019-10-16 14:23:17": not a valid representation (error: Failed to parse Date value '2019-10-16 14:23:17': Unparseable date: "2019-10-16 14:23:1
7")

当前的解决方法

由于我不想污染启动器,我当前的解决方法是扩展类并使用正确的 JsonFormat 创建本地 feign-client 和本地 pojo:

public class DocumentMetaDataFix extends DocumentMetaData {
    @JsonFormat(
        shape = Shape.STRING,
        pattern = "yyyy-MM-dd HH:mm:ss"
    )
    private Date creationDate;
    @JsonFormat(
        shape = Shape.STRING,
        pattern = "yyyy-MM-dd HH:mm:ss"
    )

修复失败

我在我的配置类中尝试了以下操作,以尝试从另一个路径影响反序列化。但是,永远不会调用 DocumentMetaDataSerializer。调用 ObjectMapper bean。

  @Configuration
    @EnableSpringDataWebSupport
    @RequiredArgsConstructor
    public class MyConfig extends WebMvcConfigurerAdapter {

   @Bean
    public Jackson2ObjectMapperBuilderCustomizer addCustomBigDecimalDeserialization() {
        return new Jackson2ObjectMapperBuilderCustomizer() {

            @Override
            public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
                jacksonObjectMapperBuilder.deserializerByType(DocumentMetaData.class, new DocumentMetaDataDeserializer());
            }

        };
    }


    @Primary
    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);
        mapper.setDateFormat(new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"));
        //mapper.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, true);
        return mapper;
    }

    @Bean
    public Module dynamoDemoEntityDeserializer() {
        SimpleModule module = new SimpleModule();
        module.addDeserializer(DocumentMetaData.class, new DocumentMetaDataDeserializer());
        return module;
    }

    public static class DocumentMetaDataDeserializer extends JsonDeserializer<DocumentMetaData> {
        @Override
        public DocumentMetaData deserialize(JsonParser jp, DeserializationContext ctxt)
                throws IOException, JsonProcessingException {
            // return DynamoDemoEntity instance;

            JsonNode node = jp.getCodec().readTree(jp);


            return null;
        }

        public DocumentMetaData deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer t) throws IOException {
            JsonNode node = jp.getCodec().readTree(jp);


            return null;
        }

    }

完整的堆栈跟踪

Caused by: com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.util.Date` from String "2019-10-16 14:23:17": not a valid representation (error: Failed to parse Date value '2019-10-16 14:23:17': Unparseable date: "2019-10-16 14:23:1
7")
 at [Source: (ByteArrayInputStream); line: 1, column: 580] (through reference chain: eu.europa.ec.nova.documentstore.DocumentMetaData["creationDate"])
        at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:67)
        at com.fasterxml.jackson.databind.DeserializationContext.weirdStringException(DeserializationContext.java:1548)
        at com.fasterxml.jackson.databind.DeserializationContext.handleWeirdStringValue(DeserializationContext.java:910)
        at com.fasterxml.jackson.databind.deser.std.StdDeserializer._parseDate(StdDeserializer.java:524)
        at com.fasterxml.jackson.databind.deser.std.StdDeserializer._parseDate(StdDeserializer.java:467)
        at com.fasterxml.jackson.databind.deser.std.DateDeserializers$DateBasedDeserializer._parseDate(DateDeserializers.java:195)
        at com.fasterxml.jackson.databind.deser.std.DateDeserializers$DateDeserializer.deserialize(DateDeserializers.java:285)
        at com.fasterxml.jackson.databind.deser.std.DateDeserializers$DateDeserializer.deserialize(DateDeserializers.java:268)
        at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:127)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
        at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
        at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3084)
        at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:237)
        ... 70 common frames omitted

那么,有什么想法吗? 我已经在项目中搜索了对 Jackson 的引用,以防我的项目中还有其他原因导致此问题。

我将尝试进入 ObjectMapper 并尝试从堆栈中调试 ObjectMapper.java:3084 中配置的当前参数/字段:

at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3084)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:237)
... 67 common frames omitted

更新

我在 objectmapper 构造函数中添加了一个断点,并且看到它正在从多个位置初始化。这让我怀疑 spring-boot 没有使用我的 ObjectMapper。相反,它使用的是从 MappingJackson2HttpMessageConverter 调用的内部弹簧。

<init>:480, ObjectMapper
build:606, Jackson2ObjectMapperBuilder
<init>:59, MappingJackson2HttpMessageConverter
<init>:74, AllEncompassingFormHttpMessageConverter

因此,我将根据我从以下网址找到的结果尝试覆盖这个内部弹簧:How to customise the Jackson JSON mapper implicitly used by Spring Boot?

但这也失败了。

参考文献

  1. Is it possible to configure Jackson custom deserializers at class level for different data types?
  2. https://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html#howto-customize-the-jackson-objectmapper
  3. https://www.baeldung.com/jackson-deserialization
  4. 非常有用: https://mostafa-asg.github.io/post/customize-json-xml-spring-mvc-output/
  5. How to customise Jackson in Spring Boot 1.4

更新 - 最终尝试列表

它仍然失败并出现错误。

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder.serializationInclusion(JsonInclude.Include.NON_NULL);
    builder.propertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
    builder.serializationInclusion(Include.NON_EMPTY);
    builder.indentOutput(true).dateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
    converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
    converters.add(new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));

    //converters.add(cmsaMessageConverter());
    converters.add(new StringHttpMessageConverter());
    converters.add(new FormHttpMessageConverter());
    converters.add(new MappingJackson2HttpMessageConverter());
}


@Bean
public Jackson2ObjectMapperBuilderCustomizer addCustomBigDecimalDeserialization() {
    return new Jackson2ObjectMapperBuilderCustomizer() {

        @Override
        public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
            jacksonObjectMapperBuilder.deserializerByType(DocumentMetaData.class, new DocumentMetaDataDeserializer());
        }

    };
}


@Primary
@Bean
public ObjectMapper objectMapper() {
    ObjectMapper mapper = new ObjectMapper();
    mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);
    mapper.setDateFormat(new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"));
    //mapper.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, true);
    return mapper;
}

@Bean
public Module dynamoDemoEntityDeserializer() {
    SimpleModule module = new SimpleModule();
    module.addDeserializer(DocumentMetaData.class, new DocumentMetaDataDeserializer());
    return module;
}

public static class DocumentMetaDataDeserializer extends JsonDeserializer<DocumentMetaData> {
    @Override
    public DocumentMetaData deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
        // return DynamoDemoEntity instance;

        JsonNode node = jp.getCodec().readTree(jp);


        return null;
    }

    public DocumentMetaData deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer t) throws IOException {
        JsonNode node = jp.getCodec().readTree(jp);


        return null;
    }

}

它仍然失败并出现错误。

【问题讨论】:

    标签: spring-boot jackson spring-cloud json-deserialization spring-cloud-feign


    【解决方案1】:

    尝试使用 LocalDateTime, 这就是我正在做的和为我工作的事情

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime date;
    

    【讨论】:

      猜你喜欢
      • 2020-09-13
      • 1970-01-01
      • 1970-01-01
      • 2018-01-12
      • 1970-01-01
      • 2012-07-01
      • 1970-01-01
      • 2020-02-27
      • 1970-01-01
      相关资源
      最近更新 更多