SpringBoot 定义 Json 序列化/反序列化 LocalDateTime 的格式

解决JSON parse error Cannot deserialize value of type `java.time.LocalDateTime` from String 问题

在SpringBoot 中使用LocalDateTime 时,接口报错

org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.time.LocalDateTime` from String "2008-11-10 07:13:16": Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '2008-11-10 07:13:16' could not be parsed at index 10; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.LocalDateTime` from String "2008-11-10 07:13:16": Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '2008-11-10 07:13:16' could not be parsed at index 10

默认情况下,Jackson(Spring Boot使用的JSON处理器)期望的日期时间格式是ISO 8601格式(例如:2008-11-10T07:13:16),而不是带有空格和没有’T’字符分隔日期和时间的格式。

方法0:配置文件

spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

这种方式主要用于旧版的java.util.Datejava.sql.Time等类型的格式化。对于 LocalDateTime 无效

方法 1: 修改输入数据格式

确保输入的日期时间字符串符合ISO 8601标准,例如:“2008-11-10T07:13:16”,但是很显然,这种方法通常是不会使用的

方法 2:使用@JsonFormat 注解

直接在实体类的相应字段上标记@JsonFormat 注解

public class YourEntityClass {
    
    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
    private LocalDateTime dateTimeField;
    
    // getter and setter
}

这种方法非常快,但在大量实体使用了LocalDateTime 时,需要在所有实体上分别进行处理,在这种情况下,我们一般希望进行全局处理,而不是一个个标记。

方法 3:自定义ObjectMapper配置

可以配置Spring Boot中的Jackson ObjectMapper来接受并解析不同的日期格式。

@Configuration
public class JacksonConfig {

    @Bean
    public ObjectMapper objectMapper() {
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(javaTimeModule);
        return objectMapper;
    }
}

在这种方法中,我们用自定义的ObjectMapper bean 覆盖了系统默认的ObjectMapper。这可能产生额外的担忧,默认的ObjectMapper是否含有一些默认配置,是我们自定义的ObjectMapper bean 所没配置的?这里需要深入研究一下。

方法 4:Jackson2ObjectMapperBuilderCustomizer

这种方法允许你在不直接创建ObjectMapper Bean的情况下对ObjectMapper进行自定义,适合用于添加模块、序列化器、反序列化器等操作。

@Configuration
public class JacksonConfig implements Jackson2ObjectMapperBuilderCustomizer {

    @Override
    public void customize(org.springframework.http.converter.json.Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
        JavaTimeModule module = new JavaTimeModule();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
        jacksonObjectMapperBuilder.modules(module);
    }
}

或者定义Jackson2ObjectMapperBuilderCustomizer Bean

@Configuration
public class JacksonConfig {

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
        return builder -> {
            JavaTimeModule module = new JavaTimeModule();
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
            builder.modules(module);
        };
    }
}

方法 5:定义Jackson2ObjectMapperBuilder Bean

你可以通过创建一个Jackson2ObjectMapperBuilder类型的Bean来实现全局配置。这样做的好处是能够集中处理所有需要自定义的Jackson配置,包括但不限于日期格式的设定。

@Configuration
public class JacksonGlobalConfig {

    @Bean
    public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
        JavaTimeModule module = new JavaTimeModule();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        
        // 添加LocalDateTime的序列化器
        module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
        // 如果需要,还可以添加其他类型的序列化器或反序列化器
        
        return new Jackson2ObjectMapperBuilder()
                .modules(module)
                // 你可以在这里添加更多的配置选项
                ;
    }
}

方法 6:实现WebMvcConfigurer接口

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        for (HttpMessageConverter<?> converter : converters) {
            if (converter instanceof MappingJackson2HttpMessageConverter) {
                ObjectMapper objectMapper = ((MappingJackson2HttpMessageConverter) converter).getObjectMapper();
                
                // 添加JavaTimeModule并设置LocalDateTime的序列化格式
                JavaTimeModule javaTimeModule = new JavaTimeModule();
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
                objectMapper.registerModule(javaTimeModule);
            }
        }
    }
}