前后端关于LocalDateTime的转换问题

8,199 阅读3分钟

问题:

    1.想返回指定的格式,比如时间戳或者指定的字符串格式。
    2.想接收指定的格式,比如时间戳或者字符串(默认要带T才行,可以设置成不带T)。
    3.想全局设置。

原理:

    重新定义LocalDateTime的序列化方式和反序列化方式。
    序列化: 把对象转化为字节。 (指定返回格式)
    反序列化: 把字节转化为对象。(接收指定格式)
    
    LocalDateTime序列化与反序列化的核心类:
    com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer的deserialize方法
    com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer的serialize方法
    
    ObjectMapper 给 指定类 设置 序列化方式 
    

文档:

    查看源码

步骤:

    1.创建LocalDateTimeSerializer类(为了返回时间戳)
    2.重定义mapperObject的Bean.

1 创建LocalDateTimeSerializer类(自定义序列化方式,返回时间戳):

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;

/**
 * 处理localdatetime 不返回时间戳的问题
 *
 * @author dripy
 * @date 2019/12/17 20:08
 */
public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
    @Override
    public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
        jsonGenerator.writeNumber(localDateTime.toInstant(ZoneOffset.of("+8")).toEpochMilli());
    }
}

2 重定义mapperObject的Bean:


import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

/**
 * @author dripy
 * @date 2019/12/17 20:08
 */
@Configuration
public class Config {
    /**
     * 设置localdatetime的序列化与反序列化的方式
     *
     * @return
     */
    @Bean(name = "mapperObject")
    public ObjectMapper getObjectMapper() {
        ObjectMapper om = new ObjectMapper();
        JavaTimeModule javaTimeModule = new JavaTimeModule();

        // "下面两个根据实际情况选择 看要返回什么格式"
        // "返回时间戳 注意这里的LocalDateTimeSerializer是上面自己实现的一个"
        // javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());

        // "返回指定字符串格式 这里的LocalDateTimeSerializer是com.fasterxml.jackson.datatype.jsr310.ser下的"
        javaTimeModule.addSerializer(LocalDateTime.class, new com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

        // 这个反序列化。接受前端传来的格式
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

        // 这两个得加上。不然他俩默认返回了List结构
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));

        om.registerModule(javaTimeModule);
        return om;
    }
}

补充:

    核心类:
    com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer的deserialize方法
    com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer的serialize方法
    
    可尝试在这两个方法中打上断点。查看请求与返回的时候使用的是什么格式的DateTimeFormatter

    尽然知道了原理。就知道怎么改变了。
    1.接收yyyy-MM-dd HH:mm:ss格式
    2.接收时间戳格式
    3.返回yyyy-MM-dd HH:mm:ss格式
    4.返回时间戳格式


补充2:

    当实现了implements WebMvcConfigurer的时候,必须手动设置进去。
   @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(getObjectMapper());
        converters.add(converter);
    }
    // 手动加入类型序列化。
    public ObjectMapper getObjectMapper() {
        ObjectMapper om = new ObjectMapper();
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));

        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));

        om.registerModule(javaTimeModule);
        return om;
    }

补充3: 当用LocalDateTime用作get请求传参的时候

    get请求的话, 不走反序列化,而是直接String的值塞给对应的类型,默认是没有转换器,会抛出异常。
    配置了转换器后,当用LocalDateTime接收get请求参数的时候,就会进入这个转换器中
    /**
     * get方式的参数 定义String与对应类型的转换方式
     *
     * @return
     */
    @Bean
    public Converter<String, LocalDateTime> LocalDateTimeConvert() {
        return new Converter<String, LocalDateTime>() {
            @Override
            public LocalDateTime convert(String source) {
                if (StringUtils.isEmpty(source)) {
                    return null;
                }
                return LocalDateTime.parse(source, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            }

        };
    }

补充4: spirngboot 默认使用的序列化方式就是 jackson

  重点就是 ObjectMapper对象。