SpringBoot JSON全局日期格式转换器实现方式
作者:fengyehongWorld 发布时间:2021-10-03 10:07:28
需求
前台有日期字符串的数据,提交到后台。后台实体类使用Date属性接收。
日期字符串有多种格式,需要用一个转换器将合法的日期字符串格式转换为Date类型。
分析
当前台的提交数据的Content-Type
为application/json;charset=utf-8
,后台使用@RequestBody
来接收数据的时候,使用此转换方式。
一. 前期准备
1.1 日期正则注解
用来标记日期字符串所对应的正则表达式
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface DatePattern {
String value();
}
1.2 日期格式定数
指定所有支持的日期格式
public final class DateFormatPart {
@DatePattern("^\\d{4}$")
public static final String YYYY = "yyyy";
@DatePattern("^\\d{4}\\d{2}$")
public static final String YYYYMM = "yyyyMM";
@DatePattern("^\\d{4}/\\d{2}$")
public static final String YYYYMM_SLASH = "yyyy/MM";
@DatePattern("^\\d{4}\\d{1,2}\\d{1,2}$")
public static final String YYYYMMDD = "yyyyMMdd";
@DatePattern("^\\d{4}/\\d{2}/\\d{2}$")
public static final String YYYYMMDD_SLASH = "yyyy/MM/dd";
@DatePattern("[0-9]+\\u5e74[0-9]+\\u6708[0-9]+\\u65e5$")
public static final String YYYYMMDD_JP = "yyyy年MM月dd日";
@DatePattern("^\\d{4}\\d{1,2}\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}$")
public static final String YYYYMMDD_HHMMSS = "yyyyMMdd HH:mm:ss";
@DatePattern("^\\d{4}/\\d{2}/\\d{2} \\d{1,2}:\\d{1,2}:\\d{1,2}$")
public static final String YYYYMMDD_HHMMSS_SLASH = "yyyy/MM/dd HH:mm:ss";
}
1.3 日期转换工具类
从日期格式定数类中获取所有的属性值和该属性上所标记的正则注解,通过反射来映射为map。
如果有需要增删的日期格式的话,只需要修改日期格式定数即可,便于维护。
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Field;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
public final class DateUtil {
// 日期格式 <==> 正则的map,使用LinkedHashMap可以避免按照顺序匹配正则表达式
private static final Map<String, String> datePatternMap = new LinkedHashMap<>();
// 日期格式List
private static final List<String> dateFormatList = new ArrayList<>();
// 使用静态代码块,可以保证只初始化一次
static {
Class<DateFormatPart> dateFormatClass = DateFormatPart.class;
// 获取所有的属性
Field[] fields = dateFormatClass.getFields();
for (Field field : fields) {
// 获取属性上的注解
DatePattern annotation = field.getAnnotation(DatePattern.class);
if (ObjectUtils.isEmpty(annotation)) {
continue;
}
// 强制让属性可以访问
ReflectionUtils.makeAccessible(field);
// 日期格式化字符串
String dateFormatStr = "";
try {
// 获取当前属性所对应的属性值
dateFormatStr = (String)field.get(dateFormatClass);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
dateFormatList.add(dateFormatStr);
// 将该属性的属性值和属性上的正则表达式放到map中
datePatternMap.put(dateFormatStr, annotation.value());
}
}
// 用所有可能支持的格式将日期字符串转换为Date格式
public static Date formatDateStrToDateAllFormat(String dateStr) {
if (ObjectUtils.isEmpty(dateStr)) {
return null;
}
try {
for (Map.Entry<String, String> mapEntry : datePatternMap.entrySet()) {
// 如果当前日期字符串不符合当前正则的话
if (!dateStr.matches(mapEntry.getValue())) {
continue;
}
return DateUtil.formatStringToDate(dateStr, mapEntry.getKey());
}
} catch (ParseException e) {
return null;
}
return null;
}
// 通过指定的格式将日期字符串转换为Date类型
public static Date formatStringToDate(String dateStr, String format) throws ParseException {
if (ObjectUtils.isEmpty(dateStr) || !dateFormatList.contains(format)) {
return null;
}
SimpleDateFormat time = new SimpleDateFormat(format);
return time.parse(dateStr);
}
}
二. 方式1-继承DateDeserializer类,重写_parseDate方法
该方式的要点是通过继承
DateDeserializer
类,然后重写_parseDate
方法实现转换功能自定义的
GlobalDateDeserializer
类需要添加到自定义的SimpleModule
模块中,然后将模块添加到ObjectMapper
中,通过jackson
来实现转换。通过设置
ObjectMapper
对象的setDateFormat
方法来实现后台数据返回到前台时的Date转String的默认格式。
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.json.PackageVersion;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.deser.std.DateDeserializers.DateDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
@Configuration
public class CustomConfig {
/*
* 自定义的全局的日期转换解析类,需要继承jackson包中的DateDeserializer
* 因为此类不会在别的地方使用了,因此直接使用内部类聚合到自定义的配置文件中
*/
private static final class GlobalDateDeserializer extends DateDeserializer {
@Override
protected Date _parseDate(JsonParser jp, DeserializationContext context) throws IOException {
return DateUtil.formatDateStrToDateAllFormat(jp.getText());
}
}
@Bean("objectMapper")
@Primary
@ConditionalOnMissingBean
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
// 创建jackson对象
ObjectMapper jackson = builder.createXmlMapper(false).build();
/*
* DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
* 在进行序列化或者反序列化的时候,
* JSON字符串中有一个字段,但是我们的对象没有这个字段的时候,该怎么处理
* 设置为true的时候,会抛出异常
* 设置为false,忽略异常继续处理
*/
jackson.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 禁用默认的「yyyy-MM-dd'T'HH:mm:ss.SSS」UTC类型
jackson.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
/*
* 设置序列化时的默认格式,即后台返回数据到前台的时候,
* Date类型数据需要转换为的字符串格式
* 如果不进行指定的话,默认使用 yyyy-MM-dd'T'HH:mm:ss.SSS 格式
*/
DateFormat dateFormat = new SimpleDateFormat(DateFormatPart.YYYYMMDD_HHMMSS_SLASH);
jackson.setDateFormat(dateFormat);
// 创建一个模块,指定该模块是用来解析 Date.class 类型数据的
SimpleModule newModule = new SimpleModule("GlobalDateDeserializer", PackageVersion.VERSION);
// 将我们创建的全局日期转换类添加到模块中,指定转换Date类型
newModule.addDeserializer(Date.class, new CustomConfig.GlobalDateDeserializer());
// 将该模块添加到jackson中
jackson.registerModule(newModule);
return jackson;
}
}
三. 方式2-继承StdDateFormat类,重写方法
parse
方法用来将日期字符串转换为Date(前台向后台传数据)forma
t方法用来将Date格式的数据转换为指定格式的字符串(后台向前台传数据)。
import java.text.FieldPosition;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.fasterxml.jackson.databind.util.StdDateFormat;
public class GlobalJsonDateConvert extends StdDateFormat {
// 静态初始化final,共享
public static final GlobalJsonDateConvert instance = new GlobalJsonDateConvert();
// 日期字符串解析为日期
@Override
public Date parse(String dateStr, ParsePosition pos) {
return getDate(dateStr);
}
@Override
public Date parse(String dateStr) {
return getDate(dateStr);
}
// 使用自定义的日期转换工具类将所有可能支持的日期字符串转换为Date格式
private Date getDate(String dateStr) {
return DateUtil.formatDateStrToDateAllFormat(dateStr);
}
/*
* 设置序列化时的默认格式,即后台返回数据到前台的时候,
* Date类型数据需要转换为的字符串格式
* 如果不进行指定的话,默认使用 yyyy-MM-dd'T'HH:mm:ss.SSS 格式
*/
@Override
public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition){
SimpleDateFormat sdf = new SimpleDateFormat(DateFormatPart.YYYYMMDD_HHMMSS_SLASH);
return sdf.format(date, toAppendTo, fieldPosition);
}
@Override
public GlobalJsonDateConvert clone() {
super.clone();
return new GlobalJsonDateConvert();
}
}
3.1 MappingJackson2HttpMessageConverter方式
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
@Configuration
public class CustomConfig {
// JSON格式 全局日期转换器配置
@Bean
public MappingJackson2HttpMessageConverter createMappingJackson2HttpMessageConverter() {
/*
* 通过MappingJackson2HttpMessageConverter得到的ObjectMapper,
* 已经默认把 DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES 给关闭了
*/
MappingJackson2HttpMessageConverter jacksonConverter = new MappingJackson2HttpMessageConverter();
jacksonConverter.getObjectMapper().setDateFormat(GlobalJsonDateConvert.instance);
return jacksonConverter;
}
}
3.2 ObjectMapper方式
通过Jackson2ObjectMapperBuilder创建ObjectMapper对象,然后将我们定义的转换器GlobalJsonDateConvert
放到ObjectMapper对象中
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
@Configuration
public class CustomConfig {
@Bean("objectMapper")
@Primary
@ConditionalOnMissingBean
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.build();
objectMapper.setDateFormat(GlobalJsonDateConvert.instance);
return objectMapper;
}
}
3.3 Jackson2ObjectMapperBuilder方式
将我们定义的转换器GlobalJsonDateConvert
放到Jackson2ObjectMapperBuilder
对象中
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
@Configuration
public class CustomConfig {
@Bean
public Jackson2ObjectMapperBuilder objectMapper() {
Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder = new Jackson2ObjectMapperBuilder();
jackson2ObjectMapperBuilder.dateFormat(GlobalJsonDateConvert.instance);
return jackson2ObjectMapperBuilder;
}
}
四. 效果
⏹前台JS
// 向后台传输的json数据
const jsonData = {
// 👉待处理的日期字符串数据
birthday: '2027年12月12日',
nameAA: 'jiafeitian',
hobby: '吃饭'
};
$.ajax({
url: url,
type: 'POST',
// 对象转换为json字符串
data: JSON.stringify(jsonData),
contentType: "application/json;charset=utf-8",
success: function (data, status, xhr) {
console.log(data);
}
});
⏹后台Form
import lombok.Data;
import java.util.Date;
@Data
public class Test15Form {
private String name;
private String hobby;
private String address;
// 用来接收的Date类型的数据
private Date birthday;
}
👇可以看到前台提交的日期字符串被转换为Date格式了
五. 总结
方式一中的1种方法和方式二中的3种方法,共计4中方法都可以实现全局日期格式转换器。
要点就是自定义日期转换的工具类用来处理各种可能的日期格式,并且将此工具类放到Jackson
中的ObjectMapper
对象中,上述4中方法的本质都是如此。
来源:https://blog.csdn.net/feyehong/article/details/130160529


猜你喜欢
- 将JavaDoc 注释 生成API文档1. 打开java代码,编写JavaDoc 注释,只有按照java的规范编写注释,才能很好的生成API
- 本总结我对于JAVA多线程中线程之间的通信方式的理解,主要以代码结合文字的方式来讨论线程间的通信,故摘抄了书中的一些示例代码,具体内容如下①
- public void ProcessRequest (HttpContext context) { &n
- 前言我们在对英文句子分词的时候,一般采用采用的分词器是WhiteSpaceTokenizerFactory,有一次因业务要求,需要根据某一个
- 如果需要集合中的元素何时删除或添加的信息,可以使用ObservableCollection<T>类。这个类是为WPF定义的,这样
- 前言数据库访问是web应用必不可少的部分。现今最常用的数据库ORM框架有Hibernate与Mybatis,Hibernate貌似在传统IT
- FeignClient发送post请求异常这个问题其实很基础。但是却难倒了我。记录一下在发送post请求的时候要指定消息格式正确的写法是这样
- 有时间我们在使用多线程的时候,需要取消线程的执行,可以使用CancellationTokenSource来取消对Task开辟多线程的取消如下
- 效果图,每隔1秒,变换一下时间 xml: <RelativeLayout xmlns:android="http
- 本文实例为大家分享了Android自定义View实现滑动回弹的具体代码,供大家参考,具体内容如下前言Android 页面滑动的时候的回弹效果
- 本文实例为大家分享了C语言运用函数指针数组制作计算器的具体代码,供大家参考,具体内容如下先来回顾一下概念:指针数组 —— 存放指针的数组函数
- 如题,在GitHub上找了一圈想找一个MongoDB的的ORM框架,未偿所愿,就去翻了翻官网(https://docs.mongodb.co
- 简介String是我们最常用的一个类,和普通java类一样其对象会存在java堆中。但是String类有其特殊之处,可以通过new方法生成,
- 许多开发人员对异步代码和多线程以及它们的工作原理和使用方法都有错误的认识。在这里,你将了解这两个概念之间的区别,并使用c#实现它们。我:“服
- 前言 spring事务管理包含两种情况,编程式事务、声明
- java 使用foreach遍历集合元素的实例1 代码示例import java.util.*; public class ForeachT
- 标题栏中的返回按钮在实际使用中用的比较多,今天就来讲讲我在项目开发中的使用经历,话不多说,还是直接上源码,上源码是最给力的。一、 编写自定义
- 说起空间动态、微博的点赞效果,网上也是很泛滥,各种实现与效果一大堆。而详细实现的部分,讲述的也是参差不齐,另一方面估计也有很多大侠也不屑一顾
- 在本文中,我们将通过用C#重构一个非常简单的代码示例来解释依赖注入和IoC容器。 简介:依赖注入和IoC乍一看可能相当复杂,但它们
- 源代码:http://github.com/lovewenyo/HttpDemo1. HttpURLConnection使用JDK原生提供的