Python 官方文档:入门教程 => 点击学习
目录背景约定反序列化局部处理反序列化全局处理序列化局部处理全局处理推荐配置背景 前后端进行时间类型的传递时,往往是 前端传递时间格式的字符串,后端反序列化成对应的时间类型后端返回数据
前后端进行时间类型的传递时,往往是
spring 的生态中已经为我们提供了对应的解决方案
如下是对本文的讨论背景做出的约定
时间格式字符串->时间类型
常见于前端传参,一般分为4种情况
Content-Type : application/json
Content-Type : application/x-www-fORM-urlencoded
Content-Type : multipart/form-data
以上四种最常见情况,每种数据编码格式都不一样,因此对应的反序列化方法也不同
在 Spring web 生态中,对于 Post + Content-Type : application/json
的方式 一般是用 json 工具进行反序列化,例如 Spring 自带的 jackson,抑或是阿里巴巴的 fastjson
而其它非 json 提交的情况, json 工具就派不上用场了,Spring 提供了额外的反序列化方式,来处理这些情况
局部处理反序列化的好处在于,粒度更细,使用更灵活,在 Spring web 生态中有两种局部处理方式,来处理上述4种常见情况
@JsonFormat 或 @JSONField
@JsonFormat
或 @JSONField
注解都可以用在时间类型的字段上,用来对该字段提供反序列化支持,例如
@JSONField(format="yyyy-MM-dd HH:mm:ss")
private Date date;
@JSONField(format="yyyy-MM-dd HH:mm:ss")
private LocalDateTime localDateTime4;
@JSONField(format="yyyy-MM-dd")
private LocalDate localDate;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date date;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime localDateTime4;
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate localDate;
注意 @JsonFormat
或 @JSONField
注解,只能用于json(Post + Content-Type : application/json
)提交的反序列化,表单提交或者Get提交是不支持的
// 这种带 @RequestBody 的 Pojo ,内部的 时间类型字段就可以使用 @JsonFormat 或 @JSONField
@PostMapping("/json")
public Pojo json(@RequestBody Pojo pojo) {
return pojo;
}
@JsonFormat
、 @JSONField
的区别与使用@JsonFormat
注解来源于 Spring web 自带 jackson,无需配置直接可以使用@JSONField
注解来源于阿里巴巴的 fastjson,需要进行配置,用 fastjson 替换掉Spring web 默认使用的 jackson 之后,才能使用。
@DateTimeFormat
@DateTimeFormat
是 Spring web 提供的针对非 json 提交,如
Content-Type : application/x-www-form-urlencoded
Content-Type : multipart/form-data
等方式时,时间类型的反序列化解决方案
// Get 传参的 Pojo ,内部的 时间类型字段可以使用 @DateTimeFormat 进行反序列化
@GetMapping
public Pojo get(Pojo pojo) {
return pojo;
}
// 表单传参的 Pojo ,内部的 时间类型字段可以使用 @DateTimeFormat 进行反序列化
@PostMapping
public Pojo post(Pojo pojo) {
return pojo;
}
@DateTimeFormat
的用法
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date date;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime localDateTime;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate localDate;
Spring web 对于 json 传参 使用的是 HttpMessageConverter<T>
转换类,而时间字符串作为普通请求参数传入时,转换用的是 Converter<S, T>
, Converter 的不同,意味着处理方式也不同。
非 json 传参的反序列化全局处理
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.TimeZone;
@Configuration
public class DateConverterConfig {
private static final String DATE_REGEX = "[1-9]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])";
private static final String TIME_REGEX = "(20|21|22|23|[0-1]\d):[0-5]\d:[0-5]\d";
private static final String DATE_TIME_REGEX = DATE_REGEX + "\s" + TIME_REGEX;
private static final String DATE_T_TIME_REGEX = DATE_REGEX + "T" + TIME_REGEX;
private static final String DATE_T_TIME_MS_REGEX = DATE_REGEX + "T" + TIME_REGEX + ".\d{3}";
private static final String TIME_STAMP_REGEX = "1\d{12}";
private static final String YEAR_MONTH_REGEX = "[1-9]\d{3}-(0[1-9]|1[0-2])";
private static final String YEAR_MONTH_PATTERN = "yyyy-MM";
private static final String DEFAULT_DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
private static final String DEFAULT_DATETIME_ISO_PATTERN = "yyyy-MM-ddTHH:mm:ss";
private static final String DEFAULT_DATETIME_MS_ISO_PATTERN = "yyyy-MM-ddTHH:mm:ss.SSS";
private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
private static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
private SimpleDateFormat getSimpleDateFormat(String pattern){
SimpleDateFormat df = new SimpleDateFormat(pattern);
System.out.println(TimeZone.getDefault());
df.setTimeZone(TimeZone.getTimeZone ("GMT"));
return df;
}
@Bean
public Converter<String, Date> dateConverter() {
return new Converter<String, Date>() {
@SuppressWarnings("NullableProblems")
@Override
public Date convert(String source) {
if (StringUtils.isEmpty(source)) {
return null;
}
// 13位毫秒值 -> Date
if (source.matches(TIME_STAMP_REGEX)) {
return new Date(Long.parseLong(source));
}
try {
// yyyy-MM-dd HH:mm:ss -> Date
if (source.matches(DATE_TIME_REGEX)) {
return getSimpleDateFormat(DEFAULT_DATETIME_PATTERN).parse(source);
}
// yyyy-MM-dd -> Date
if (source.matches(DATE_REGEX)) {
return getSimpleDateFormat(DEFAULT_DATE_FORMAT).parse(source);
}
// yyyy-MM -> Date
if (source.matches(YEAR_MONTH_REGEX)) {
return getSimpleDateFormat(YEAR_MONTH_PATTERN).parse(source);
}
} catch (ParseException e) {
throw new RuntimeException(e);
}
return null;
}
};
}
@Bean
public Converter<String, LocalDateTime> localDateTimeConverter() {
return new Converter<String, LocalDateTime>() {
@SuppressWarnings("NullableProblems")
@Override
public LocalDateTime convert(String source) {
if (StringUtils.isEmpty(source)) {
return null;
}
// 13位毫秒值 -> LocalDateTime
if (source.matches(TIME_STAMP_REGEX)) {
Instant instant = Instant.ofEpochMilli(Long.parseLong(source));
ZoneId zone = ZoneId.systemDefault();
return LocalDateTime.ofInstant(instant, zone);
}
// yyyy-MM-dd HH:mm:ss -> LocalDateTime
if (source.matches(DATE_TIME_REGEX)) {
return LocalDateTime.parse(source, DateTimeFormatter.ofPattern(DEFAULT_DATETIME_PATTERN));
}
// yyyy-MM-ddTHH:mm:ss -> LocalDateTime
if (source.matches(DATE_T_TIME_REGEX)) {
return LocalDateTime.parse(source, DateTimeFormatter.ofPattern(DEFAULT_DATETIME_ISO_PATTERN));
}
// yyyy-MM-ddTHH:mm:ss.SSS -> LocalDateTime
if (source.matches(DATE_T_TIME_MS_REGEX)) {
return LocalDateTime.parse(source, DateTimeFormatter.ofPattern(DEFAULT_DATETIME_MS_ISO_PATTERN));
}
return null;
}
};
}
@Bean
public Converter<String, LocalDate> localDateConverter() {
return new Converter<String, LocalDate>() {
@SuppressWarnings("NullableProblems")
@Override
public LocalDate convert(String source) {
if (StringUtils.isEmpty(source)) {
return null;
}
// 13位毫秒值 -> LocalDate
if (source.matches(TIME_STAMP_REGEX)) {
Instant instant = Instant.ofEpochMilli(Long.parseLong(source));
ZoneId zone = ZoneId.systemDefault();
return LocalDateTime.ofInstant(instant, zone).toLocalDate();
}
// yyyy-MM-dd -> LocalDate
if (source.matches(DATE_REGEX)) {
return LocalDate.parse(source, DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT));
}
return null;
}
};
}
@Bean
public Converter<String, LocalTime> localTimeConverter() {
return new Converter<String, LocalTime>() {
@SuppressWarnings("NullableProblems")
@Override
public LocalTime convert(String source) {
if (StringUtils.isEmpty(source)) {
return null;
}
// HH:mm:ss -> LocalTime
return LocalTime.parse(source, DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT));
}
};
}
}
注意:使用了自定义参数转化器(Converter),Spring 会优先使用该方式进行处理,@DateTimeFormat
注解将不生效!!! 这两种方案是不兼容!!!
json 传参的反序列化全局处理
如果是 json 传参,反序列化时,可以通过配置 json 工具进行全局处理。
以 Spring web 自带的 jackson 为例,它配置全局时间格式化时, java.util 包中的时间类型与 java 8 之后引入了 java.time 包中的时间类型,要分开配置
全局配置 java.util 包中的时间类型的反序列化格式
常用的时间类型包括
通过配置文件配置,以 yaml 为例
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
这是最简单的配置方式,也可以采取其他方式,这里就不例举了
全局配置 java.time 包中的时间类型的反序列化格式
常用的时间类型包括
@Configuration
public class JacksonConfig {
private static final String DATE_FORMAT = "yyyy-MM-dd";
private static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return builder -> {
builder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));
builder.deserializerByType(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DATE_FORMAT)));
};
}
}
时间类型->时间格式字符串
常见于后端返回,现在后端接口,返回格式一般都采用 json ,因此处理起来比反序列化要简单
在要返回类的时间类型字段上使用 @JsonFormat
或 @JSONField
注解,来对该字段提供序列化支持。例如
@JSONField(format="yyyy-MM-dd HH:mm:ss")
private Date date;
@JSONField(format="yyyy-MM-dd HH:mm:ss")
private LocalDateTime localDateTime4;
@JSONField(format="yyyy-MM-dd")
private LocalDate localDate;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date date;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime localDateTime4;
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate localDate;
是的 @JsonFormat
或 @JSONField
注解,既能用于json提交的反序列化,也能用于返回 json 的序列化
同样,使用 @JSONField
注解之前,需要先进行配置。用 fastjson 替换掉Spring web 默认使用的 jackson 之后,才能使用。
以 Spring web 自带的 jackson 为例,它配置全局时间格式化时, java.util 包中的时间类型与 java 8 之后引入了 java.time 包中的时间类型,要分开配置
全局配置 java.util 包中的时间类型的序列化格式
常用的时间类型包括
通过配置文件配置
以 yaml 为例
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
这是最简单的配置方式,也可以采取其他方式,这里就不例举了
全局配置 java.time 包中的时间类型的序列化格式
常用的时间类型包括
@Configuration
public class JacksonConfig {
private static final String DATE_FORMAT = "yyyy-MM-dd";
private static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return jacksonObjectMapperBuilder -> {
// 配置 Jackson 序列化 LocalDate、LocalDateTime 时使用的格式
jacksonObjectMapperBuilder.serializers(new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_FORMAT)));
jacksonObjectMapperBuilder.serializers(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));
};
}
}
对时间类型进行 序列化 时, 由于统一返回的都是 json 格式,推荐进行全局配置,实际开发过程中,如果遇到特殊情况,再选择用 @JsonFormat
进行局部覆盖
对时间类型进行 反序列化 时
@JsonFormat
进行局部覆盖。 Converter<S, T>
后,@DateTimeForma
注解将失效。看情况自行选择。配置一览
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
@Configuration
public class JacksonConfig {
private static final String DATE_FORMAT = "yyyy-MM-dd";
private static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return jacksonObjectMapperBuilder -> {
// 配置 Jackson 序列化 LocalDate、LocalDateTime 时使用的格式
jacksonObjectMapperBuilder.serializers(new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_FORMAT)));
jacksonObjectMapperBuilder.serializers(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));
// 配置 Jackson 反序列化 LocalDate、LocalDateTime 时使用的格式
jacksonObjectMapperBuilder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));
jacksonObjectMapperBuilder.deserializerByType(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DATE_FORMAT)));
};
}
}
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.TimeZone;
@Configuration
public class DateConverterConfig {
private static final String DATE_REGEX = "[1-9]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])";
private static final String TIME_REGEX = "(20|21|22|23|[0-1]\d):[0-5]\d:[0-5]\d";
private static final String DATE_TIME_REGEX = DATE_REGEX + "\s" + TIME_REGEX;
private static final String DATE_T_TIME_REGEX = DATE_REGEX + "T" + TIME_REGEX;
private static final String DATE_T_TIME_MS_REGEX = DATE_REGEX + "T" + TIME_REGEX + ".\d{3}";
private static final String TIME_STAMP_REGEX = "1\d{12}";
private static final String YEAR_MONTH_REGEX = "[1-9]\d{3}-(0[1-9]|1[0-2])";
private static final String YEAR_MONTH_PATTERN = "yyyy-MM";
private static final String DEFAULT_DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
private static final String DEFAULT_DATETIME_ISO_PATTERN = "yyyy-MM-ddTHH:mm:ss";
private static final String DEFAULT_DATETIME_MS_ISO_PATTERN = "yyyy-MM-ddTHH:mm:ss.SSS";
private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
private static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
private SimpleDateFormat getSimpleDateFormat(String pattern){
SimpleDateFormat df = new SimpleDateFormat(pattern);
System.out.println(TimeZone.getDefault());
df.setTimeZone(TimeZone.getTimeZone ("GMT"));
return df;
}
@Bean
public Converter<String, Date> dateConverter() {
return new Converter<String, Date>() {
@SuppressWarnings("NullableProblems")
@Override
public Date convert(String source) {
if (StringUtils.isEmpty(source)) {
return null;
}
// 13位毫秒值 -> Date
if (source.matches(TIME_STAMP_REGEX)) {
return new Date(Long.parseLong(source));
}
try {
// yyyy-MM-dd HH:mm:ss -> Date
if (source.matches(DATE_TIME_REGEX)) {
return getSimpleDateFormat(DEFAULT_DATETIME_PATTERN).parse(source);
}
// yyyy-MM-dd -> Date
if (source.matches(DATE_REGEX)) {
return getSimpleDateFormat(DEFAULT_DATE_FORMAT).parse(source);
}
// yyyy-MM -> Date
if (source.matches(YEAR_MONTH_REGEX)) {
return getSimpleDateFormat(YEAR_MONTH_PATTERN).parse(source);
}
} catch (ParseException e) {
throw new RuntimeException(e);
}
return null;
}
};
}
@Bean
public Converter<String, LocalDateTime> localDateTimeConverter() {
return new Converter<String, LocalDateTime>() {
@SuppressWarnings("NullableProblems")
@Override
public LocalDateTime convert(String source) {
if (StringUtils.isEmpty(source)) {
return null;
}
// 13位毫秒值 -> LocalDateTime
if (source.matches(TIME_STAMP_REGEX)) {
Instant instant = Instant.ofEpochMilli(Long.parseLong(source));
ZoneId zone = ZoneId.systemDefault();
return LocalDateTime.ofInstant(instant, zone);
}
// yyyy-MM-dd HH:mm:ss -> LocalDateTime
if (source.matches(DATE_TIME_REGEX)) {
return LocalDateTime.parse(source, DateTimeFormatter.ofPattern(DEFAULT_DATETIME_PATTERN));
}
// yyyy-MM-ddTHH:mm:ss -> LocalDateTime
if (source.matches(DATE_T_TIME_REGEX)) {
return LocalDateTime.parse(source, DateTimeFormatter.ofPattern(DEFAULT_DATETIME_ISO_PATTERN));
}
// yyyy-MM-ddTHH:mm:ss.SSS -> LocalDateTime
if (source.matches(DATE_T_TIME_MS_REGEX)) {
return LocalDateTime.parse(source, DateTimeFormatter.ofPattern(DEFAULT_DATETIME_MS_ISO_PATTERN));
}
return null;
}
};
}
@Bean
public Converter<String, LocalDate> localDateConverter() {
return new Converter<String, LocalDate>() {
@SuppressWarnings("NullableProblems")
@Override
public LocalDate convert(String source) {
if (StringUtils.isEmpty(source)) {
return null;
}
// 13位毫秒值 -> LocalDate
if (source.matches(TIME_STAMP_REGEX)) {
Instant instant = Instant.ofEpochMilli(Long.parseLong(source));
ZoneId zone = ZoneId.systemDefault();
return LocalDateTime.ofInstant(instant, zone).toLocalDate();
}
// yyyy-MM-dd -> LocalDate
if (source.matches(DATE_REGEX)) {
return LocalDate.parse(source, DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT));
}
return null;
}
};
}
@Bean
public Converter<String, LocalTime> localTimeConverter() {
return new Converter<String, LocalTime>() {
@SuppressWarnings("NullableProblems")
@Override
public LocalTime convert(String source) {
if (StringUtils.isEmpty(source)) {
return null;
}
// HH:mm:ss -> LocalTime
return LocalTime.parse(source, DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT));
}
};
}
}
以上就是详解SpringBoot中时间类型的序列化与反序列化的详细内容,更多关于SpringBoot序列化 反序列化的资料请关注编程网其它相关文章!
--结束END--
本文标题: 详解SpringBoot中时间类型的序列化与反序列化
本文链接: https://lsjlt.com/news/193922.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-03-01
2024-03-01
2024-03-01
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0