无法在Springboot中使用Jackson反序列化自定义Date对象"没有单字符串构造函数/工厂方法"

时间:2016-10-18 20:07:00

标签: java spring-boot jackson

编辑:

为了澄清问题我已经制作了一个图表:

enter image description here

我一直在迁移自定义Jetty Web服务以使用Spring REST模板,并且我遇到了以下解串器的问题。

自定义日期格式如下所示:"2016-10-18"我需要在调用此自定义Date类型的构造函数之前将其转换为3个整数。

之前这是有效的,因为我们刚刚通过服务ObjectMapper并注册了反序列化程序,并且没有任何问题调用deserialize

现在我收到错误,因为杰克逊试图在我的自定义String类型上调用一个Date构造函数,但这种构造函数并不存在。该库不是由我维护的,因此我无法添加此构造函数。

错误:

  

"无法读取HTTP消息:   org.springframework.http.converter.HttpMessageNotReadableException:   无法读取文档:无法实例化类型的值[简单   来自String值的type,class com.intercorp.domain.date.Date]   (' 2016年9月30日&#39);没有单字符串构造函数/工厂方法

注意

注释我尝试序列化的属性有效,但我不认为这是一个解决方案,因为注册deserialiser应检测类型并调用自身。 我更感兴趣的是为什么这不起作用而不是切换到注释

@JsonDeserialize(using=JacksonConfig.CustomDateDeserializer.class)
private Date paymentDate;

我有以下Spring配置:

@Import({JacksonConfig.class})
public class WebServiceConfig {

    @Autowired
    private MappingJackson2HttpMessageConverter jacksonConverter;

    @Bean
    public MyWebService MyWebService () {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getMessageConverters().add(jacksonConverter);
        return new MyWebService(restTemplate);
    }
}

JacksonConfig.class

@Configuration
public class JacksonConfig {

    @Bean
    public MappingJackson2HttpMessageConverter jacksonConverter() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setObjectMapper(objectMapper());
        return converter;
    }

    @Bean
    @Primary
    public ObjectMapper objectMapper() {
        Jackson2ObjectMapperBuilder mapperBuilder = new Jackson2ObjectMapperBuilder();
        mapperBuilder.modules(myModule()).timeZone(TimeZone.getTimeZone("UTC"))
                     .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        return mapperBuilder.build();
    }

    @Bean
    public Module myModule() {
        SimpleModule module = new SimpleModule("myModule", new Version(0, 0, 1, null));

        module.addSerializer(new CustomDateSerializer());
        //not Java Date - custom Date...
        module.addDeserializer(Date.class, new CustomDateDeserializer());

        return module;
    }

    // breakpoints indicate this is never called
    static public class CustomDateDeserializer extends JsonDeserializer<Date> {

            @Override
            @SneakyThrows
            public Date deserialize(JsonParser jp, DeserializationContext ctxt) {
                String text = jp.getText();
                if (text == null) {
                    return null;
                }
                if (text.length() >= 10) {
                    int year = parseInt(text.substring(0, 4));
                    int month = parseInt(text.substring(5, 7));
                    int day = parseInt(text.substring(8, 10));
                    return new Date(year, month, day);
                }
                // deserialize json object
                else if (JsonToken.START_OBJECT.equals(jp.getCurrentToken())) {
                    JsonNode dateNode = jp.getCodec().readTree(jp);
                    return getDate(dateNode);
                }
                else {
                    return new Date(Integer.parseInt(text));
                }
            }
        }

}

PaymentStatus.java

package com.otpp.memberapi.service.iaccess.data.pension;

import com.intercorp.domain.date.Date;
import com.intercorp.domain.money.Money;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class PensionPaymentStatus {

    private boolean available;
    private Money netMonthly;
    //this date fails to deserialise
    private Date paymentDate;
    private boolean suspended;

}

示例JSON有效负载

{
    "available": true,
    "netMonthly": "1000.00",
    "paymentDate": "2016-10-19",
    "suspended": false
}

日期类很大,只包含Joda时间(用于Java8之前的envs)。它有几个构造函数,我试图使用的是YYYY,MM,DD整数构造函数。

1 个答案:

答案 0 :(得分:1)

似乎请求未被转换器反序列化。

RestTemplate的no-arg构造函数使用默认转换器初始化它,相关代码段:

....
if (jackson2Present) {
    this.messageConverters.add(new MappingJackson2HttpMessageConverter());
}
....

因为它使用&#39;首先赢得&#39;寻找合适的转换器的策略它永远不会使用你的,因为它位于列表的后面。

您需要更改:

RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(jacksonConverter);

对于这样的事情:

RestTemplate restTemplate = new RestTemplate(Collections.singletonList(jacksonConverter));

取决于您是否需要其他转换器。