SpringBoot JSON全局日期格式转换器实现方式

作者:fengyehongWorld 时间:2021-10-03 10:07:28 

需求

前台有日期字符串的数据,提交到后台。后台实体类使用Date属性接收。
日期字符串有多种格式,需要用一个转换器将合法的日期字符串格式转换为Date类型。

分析

当前台的提交数据的Content-Typeapplication/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(前台向后台传数据)

  • format方法用来将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格式了

SpringBoot JSON全局日期格式转换器实现方式

五. 总结

方式一中的1种方法和方式二中的3种方法,共计4中方法都可以实现全局日期格式转换器。
要点就是自定义日期转换的工具类用来处理各种可能的日期格式,并且将此工具类放到Jackson中的ObjectMapper对象中,上述4中方法的本质都是如此。

来源:https://blog.csdn.net/feyehong/article/details/130160529

标签:SpringBoot,JSON,日期格式,转换
0
投稿

猜你喜欢

  • Java如何基于IO流实现同一文件读写操作

    2023-07-30 15:06:38
  • Java并发系列之AbstractQueuedSynchronizer源码分析(共享模式)

    2022-06-03 13:39:58
  • SpringBoot整合Swagger的方法示例

    2021-05-29 02:42:16
  • C#中动态显示当前系统时间的实例方法

    2023-06-20 14:46:02
  • Java实现LeetCode(54.螺旋矩阵)

    2023-01-26 20:54:00
  • UnityShader使用Plane实现翻书效果

    2022-05-19 14:46:14
  • C# 骑士飞行棋的源码(分享)

    2021-10-11 02:54:56
  • Java输出链表倒数第k个节点

    2023-03-22 01:22:34
  • AsyncTask官方文档教程整理

    2023-07-31 20:25:08
  • C#通过属性名字符串获取、设置对象属性值操作示例

    2022-01-06 14:28:08
  • Android Studio实现智能聊天

    2021-10-03 09:56:18
  • Maven工程pom中如何定义jdk版本

    2022-02-18 08:16:13
  • Java 实战项目锤炼之IT设备固定资产管理系统的实现流程

    2023-02-06 01:31:39
  • 详解Spring Boot 定制HTTP消息转换器

    2023-11-24 20:20:51
  • java中 Set与Map排序输出到Writer详解及实例

    2022-05-01 07:23:56
  • Unity shader实现自由放大缩小效果

    2022-04-05 06:50:46
  • Android编程设计模式之迭代器模式详解

    2023-06-07 01:25:42
  • Unity实现俄罗斯方块游戏

    2023-05-30 21:07:22
  • Android下的CMD命令之关机重启及重启recovery

    2023-06-20 23:08:21
  • C#使用反射机制实现延迟绑定

    2021-06-13 22:22:42
  • asp之家 软件编程 m.aspxhome.com