SpringBoot日期序列化

在一个请求中存在一个时间字段,但是前端传过来的值格式有问题,由此引出本文。

问题:

  • 后端接收:
    @Data
    @NoArgsConstructor
    @ApiModel(value = "时间参数")
    public class DateInsert {
    
       @ApiModelProperty(value = "时间A")
       private Date a;
    
       @ApiModelProperty(value = "时间B")
       private Date b;
    }
    
  • 前端传输:
    {
        "a":"2020-12-20T02:08:39.000Z",
        "b":"2020-12-27 07:28:36"
    }
    

本文的问题是,日期格式数据前端可能会传输两种过来(格式化好的以及带T的),a、b两个字段实际上是同一个字段,此处目的是同时实现两种格式数据接收。(注意,这两个字段数据类型、注解等一致)

解决

  • 全局配置序列化
    package com.single.cong.config;
    
    import java.io.IOException;
    import java.math.BigInteger;
    import java.nio.charset.StandardCharsets;
    import java.text.SimpleDateFormat;
    import java.time.LocalDate;
    import java.time.LocalDateTime;
    import java.time.LocalTime;
    import java.time.format.DateTimeFormatter;
    import java.util.Date;
    import java.util.List;
    import java.util.TimeZone;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.http.converter.StringHttpMessageConverter;
    import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
    import org.springframework.web.servlet.config.annotation.CorsRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
    
    import com.fasterxml.jackson.core.JsonGenerator;
    import com.fasterxml.jackson.core.JsonParser;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.DeserializationContext;
    import com.fasterxml.jackson.databind.JsonDeserializer;
    import com.fasterxml.jackson.databind.JsonSerializer;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.SerializerProvider;
    import com.fasterxml.jackson.databind.module.SimpleModule;
    import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
    import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
    import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
    import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
    import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
    import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
    import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
    import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
    
    @Configuration
    public class WebMvcConfig extends WebMvcConfigurationSupport {
    
    	/** 解决返回值乱码问题 */
    	@Bean
    	public HttpMessageConverter<String> responseBodyStringConverter() {
    		StringHttpMessageConverter converter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
    		return converter;
    	}
    
    	/** 修改StringHttpMessageConverter默认配置 */
    	@Override
    	public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    		converters.add(responseBodyStringConverter());
    
    		// 日期序列化配置
    		
    		JavaTimeModule javaTimeModule = new JavaTimeModule();
    		/** 序列化配置,针对java8 时间 **/
    		javaTimeModule.addSerializer(LocalDateTime.class,
    				new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    		javaTimeModule.addSerializer(LocalDate.class,
    				new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
    		javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
    
    		/** 反序列化配置,针对java8 时间 **/
    		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")));
    		javaTimeModule.addDeserializer(LocalTime.class,
    				new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
    		// Date序列化和反序列化
    		javaTimeModule.addSerializer(Date.class, new JsonSerializer<Date>() {
    			@Override
    			public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
    					throws IOException {
    				SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    				String formattedDate = formatter.format(date);
    				jsonGenerator.writeString(formattedDate);
    			}
    		});
    		javaTimeModule.addDeserializer(Date.class, new JsonDeserializer<Date>() {
    			@Override
    			public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
    					throws IOException, JsonProcessingException {
    				SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    				String date = jsonParser.getText();
    				// 处理数据在这里
    				try {
    					if (date.contains("T")) {
    						date = date.replaceAll("T", " ").substring(0, 19);
    						// 特殊格式数据,进行日期转换
    						return format.parse(date);
    					} else {
    						// 正常数据日期转换
    						return format.parse(date);
    					}
    				} catch (Exception e) {
    					throw new RuntimeException(e);
    				}
    			}
    		});
    		
    		
    		
    
    		MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
    		ObjectMapper objectMapper = new ObjectMapper();
    		SimpleModule simpleModule = new SimpleModule();
    		/**
    		 * 将Long,BigInteger序列化的时候,转化为String,某些类里面的Long类型数据超长造成精度缺失可以在属性上使用@JsonSerialize注解
    		 */
    		simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
    		simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
    		simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
    		objectMapper.registerModule(simpleModule).registerModule(javaTimeModule);
    		// objectMapper.registerModule(simpleModule);
    		objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
    		objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
    		messageConverter.setObjectMapper(objectMapper);
    		converters.add(messageConverter);
    	}
    }
    
    注意:date = date.replaceAll("T", " ").substring(0, 19);单纯是将字符串处理一下然后再格式化,没什么说的,但是要注意,如果使用此种方式,那么系统里面所有的Date类型的数据序列化都会先走一个判断,如果系统中参数格式不固定可以使用这种方式,但是肯定会带来性能上的问题。
  • 单独序列化
    自定义类继承JsonDeserializer
    package com.single.cong.config;
    
    import java.io.IOException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    import com.fasterxml.jackson.core.JsonParser;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.DeserializationContext;
    import com.fasterxml.jackson.databind.JsonDeserializer;
    
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j
    public class MyDateSerialize extends JsonDeserializer<Date> {
    
    	@Override
    	public Date deserialize(JsonParser jsonParser, DeserializationContext ctxt)
    			throws IOException, JsonProcessingException {
    		log.info("参数数据为:[{}]", jsonParser.getText());
    		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    		String date = jsonParser.getText();
    		try {
    			if (date.contains("T")) {
    				date = date.replaceAll("T", " ").substring(0, 19);
    				// 特殊格式数据,进行日期转换
    				return format.parse(date);
    			} else {
    				// 正常数据日期转换
    				return format.parse(date);
    			}
    		} catch (Exception e) {
    			throw new RuntimeException(e);
    		}
    	}
    }
    
    使用:
    @Data
    @NoArgsConstructor
    @ApiModel(value = "时间参数")
    public class DateInsert {
    
    	@JsonDeserialize(using = MyDateSerialize.class)
    	@ApiModelProperty(value = "时间A")
    	private Date a;
    
    	@ApiModelProperty(value = "时间B")
    	private Date b;
    }
    
    这样就只需要对有问题的字段单独设置自定义的序列化方式,至于其他的字段不变。

总结

  • 做了什么
    实际上这个问题就是自己搞出来的,一般只要前后端能够明确数据格式都不会出现这个问题,本文的日期格式化问题出现在反序列化中,在将参数转换为Date类型的时候因为参数的格式问题一直导致序列化失败,所以本文其实最终只做了一件事情:在反序列化之前将参数处理成正常格式。
  • 更多
    在解决这个问题的过程中有几位网友建议将Date转为LocalDateTime类型,我查了一下,都在说LocalDateTime性能好,线程安全,所以建议以后还是使用LocalDateTime吧。

疑问

我在解决这个问题的时候看到网上很多文章写得自定义日期转换器Converter,但是不知道为什么我按照别人说的文章写了但是没用,最后还是用的上面这两个方法。

你可能感兴趣的:(SpringBoot,Date序列化,JsonDeserialize)