SpringBoot JSON全局日期格式轉(zhuǎn)換器實(shí)現(xiàn)方式
需求
前臺有日期字符串的數(shù)據(jù),提交到后臺。后臺實(shí)體類使用Date屬性接收。
日期字符串有多種格式,需要用一個(gè)轉(zhuǎn)換器將合法的日期字符串格式轉(zhuǎn)換為Date類型。
分析
當(dāng)前臺的提交數(shù)據(jù)的Content-Type
為application/json;charset=utf-8
,后臺使用@RequestBody
來接收數(shù)據(jù)的時(shí)候,使用此轉(zhuǎn)換方式。
一. 前期準(zhǔn)備
1.1 日期正則注解
用來標(biāo)記日期字符串所對應(yīng)的正則表達(dá)式
import java.lang.annotation.*; @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD}) public @interface DatePattern { String value(); }
1.2 日期格式定數(shù)
指定所有支持的日期格式
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 日期轉(zhuǎn)換工具類
- 從日期格式定數(shù)類中獲取所有的屬性值和該屬性上所標(biāo)記的正則注解,通過反射來映射為map。
- 如果有需要增刪的日期格式的話,只需要修改日期格式定數(shù)即可,便于維護(hù)。
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可以避免按照順序匹配正則表達(dá)式 private static final Map<String, String> datePatternMap = new LinkedHashMap<>(); // 日期格式List private static final List<String> dateFormatList = new ArrayList<>(); // 使用靜態(tài)代碼塊,可以保證只初始化一次 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; } // 強(qiáng)制讓屬性可以訪問 ReflectionUtils.makeAccessible(field); // 日期格式化字符串 String dateFormatStr = ""; try { // 獲取當(dāng)前屬性所對應(yīng)的屬性值 dateFormatStr = (String)field.get(dateFormatClass); } catch (IllegalAccessException e) { e.printStackTrace(); } dateFormatList.add(dateFormatStr); // 將該屬性的屬性值和屬性上的正則表達(dá)式放到map中 datePatternMap.put(dateFormatStr, annotation.value()); } } // 用所有可能支持的格式將日期字符串轉(zhuǎn)換為Date格式 public static Date formatDateStrToDateAllFormat(String dateStr) { if (ObjectUtils.isEmpty(dateStr)) { return null; } try { for (Map.Entry<String, String> mapEntry : datePatternMap.entrySet()) { // 如果當(dāng)前日期字符串不符合當(dāng)前正則的話 if (!dateStr.matches(mapEntry.getValue())) { continue; } return DateUtil.formatStringToDate(dateStr, mapEntry.getKey()); } } catch (ParseException e) { return null; } return null; } // 通過指定的格式將日期字符串轉(zhuǎn)換為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方法
- 該方式的要點(diǎn)是通過繼承
DateDeserializer
類,然后重寫_parseDate
方法實(shí)現(xiàn)轉(zhuǎn)換功能 - 自定義的
GlobalDateDeserializer
類需要添加到自定義的SimpleModule
模塊中,然后將模塊添加到ObjectMapper
中,通過jackson
來實(shí)現(xiàn)轉(zhuǎn)換。 - 通過設(shè)置
ObjectMapper
對象的setDateFormat
方法來實(shí)現(xiàn)后臺數(shù)據(jù)返回到前臺時(shí)的Date轉(zhuǎn)String的默認(rèn)格式。
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 { /* * 自定義的全局的日期轉(zhuǎn)換解析類,需要繼承jackson包中的DateDeserializer * 因?yàn)榇祟惒粫趧e的地方使用了,因此直接使用內(nèi)部類聚合到自定義的配置文件中 */ 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) { // 創(chuàng)建jackson對象 ObjectMapper jackson = builder.createXmlMapper(false).build(); /* * DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES * 在進(jìn)行序列化或者反序列化的時(shí)候, * JSON字符串中有一個(gè)字段,但是我們的對象沒有這個(gè)字段的時(shí)候,該怎么處理 * 設(shè)置為true的時(shí)候,會拋出異常 * 設(shè)置為false,忽略異常繼續(xù)處理 */ jackson.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 禁用默認(rèn)的「yyyy-MM-dd'T'HH:mm:ss.SSS」UTC類型 jackson.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); /* * 設(shè)置序列化時(shí)的默認(rèn)格式,即后臺返回?cái)?shù)據(jù)到前臺的時(shí)候, * Date類型數(shù)據(jù)需要轉(zhuǎn)換為的字符串格式 * 如果不進(jìn)行指定的話,默認(rèn)使用 yyyy-MM-dd'T'HH:mm:ss.SSS 格式 */ DateFormat dateFormat = new SimpleDateFormat(DateFormatPart.YYYYMMDD_HHMMSS_SLASH); jackson.setDateFormat(dateFormat); // 創(chuàng)建一個(gè)模塊,指定該模塊是用來解析 Date.class 類型數(shù)據(jù)的 SimpleModule newModule = new SimpleModule("GlobalDateDeserializer", PackageVersion.VERSION); // 將我們創(chuàng)建的全局日期轉(zhuǎn)換類添加到模塊中,指定轉(zhuǎn)換Date類型 newModule.addDeserializer(Date.class, new CustomConfig.GlobalDateDeserializer()); // 將該模塊添加到j(luò)ackson中 jackson.registerModule(newModule); return jackson; } }
三. 方式2-繼承StdDateFormat類,重寫方法
parse
方法用來將日期字符串轉(zhuǎn)換為Date(前臺向后臺傳數(shù)據(jù))forma
t方法用來將Date格式的數(shù)據(jù)轉(zhuǎn)換為指定格式的字符串(后臺向前臺傳數(shù)據(jù))。
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 { // 靜態(tài)初始化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); } // 使用自定義的日期轉(zhuǎn)換工具類將所有可能支持的日期字符串轉(zhuǎn)換為Date格式 private Date getDate(String dateStr) { return DateUtil.formatDateStrToDateAllFormat(dateStr); } /* * 設(shè)置序列化時(shí)的默認(rèn)格式,即后臺返回?cái)?shù)據(jù)到前臺的時(shí)候, * Date類型數(shù)據(jù)需要轉(zhuǎn)換為的字符串格式 * 如果不進(jìn)行指定的話,默認(rèn)使用 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格式 全局日期轉(zhuǎn)換器配置 @Bean public MappingJackson2HttpMessageConverter createMappingJackson2HttpMessageConverter() { /* * 通過MappingJackson2HttpMessageConverter得到的ObjectMapper, * 已經(jīng)默認(rèn)把 DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES 給關(guān)閉了 */ MappingJackson2HttpMessageConverter jacksonConverter = new MappingJackson2HttpMessageConverter(); jacksonConverter.getObjectMapper().setDateFormat(GlobalJsonDateConvert.instance); return jacksonConverter; } }
3.2 ObjectMapper方式
通過Jackson2ObjectMapperBuilder創(chuàng)建ObjectMapper對象,然后將我們定義的轉(zhuǎn)換器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方式
將我們定義的轉(zhuǎn)換器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
// 向后臺傳輸?shù)膉son數(shù)據(jù) const jsonData = { // ??待處理的日期字符串?dāng)?shù)據(jù) birthday: '2027年12月12日', nameAA: 'jiafeitian', hobby: '吃飯' }; $.ajax({ url: url, type: 'POST', // 對象轉(zhuǎn)換為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類型的數(shù)據(jù) private Date birthday; }
??可以看到前臺提交的日期字符串被轉(zhuǎn)換為Date格式了
五. 總結(jié)
方式一中的1種方法和方式二中的3種方法,共計(jì)4中方法都可以實(shí)現(xiàn)全局日期格式轉(zhuǎn)換器。
要點(diǎn)就是自定義日期轉(zhuǎn)換的工具類用來處理各種可能的日期格式,并且將此工具類放到Jackson
中的ObjectMapper
對象中,上述4中方法的本質(zhì)都是如此。
到此這篇關(guān)于SpringBoot JSON全局日期格式轉(zhuǎn)換器的文章就介紹到這了,更多相關(guān)SpringBoot JSON日期格式轉(zhuǎn)換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot實(shí)現(xiàn)設(shè)置全局和局部時(shí)間格式化
- Springboot 全局時(shí)間格式化操作
- springboot全局日期格式化的兩種方式
- Springboot 全局日期格式化處理的實(shí)現(xiàn)
- SpringBoot中?Jackson?日期的時(shí)區(qū)和日期格式問題解決
- SpringBoot中Jackson日期格式化技巧分享
- SpringBoot利用jackson格式化時(shí)間的三種方法
- SpringBoot2.0整合jackson配置日期格式化和反序列化的實(shí)現(xiàn)
- SpringBoot使用Jackson配置全局時(shí)間日期格式
相關(guān)文章
SpringBoot 如何自定義請求參數(shù)校驗(yàn)
這篇文章主要介紹了SpringBoot 如何自定義請求參數(shù)校驗(yàn)方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10springMvc異步的DeferredResult long?polling應(yīng)用示例解析
這篇文章主要為大家介紹了springMvc中DeferredResult的long?polling應(yīng)用示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03教你怎么用Java數(shù)組和鏈表實(shí)現(xiàn)棧
本篇文章為大家詳細(xì)介紹了怎么用Java數(shù)組和鏈表實(shí)現(xiàn)棧,文中有非常詳細(xì)的代碼示例及注釋,對正在學(xué)習(xí)java的小伙伴們很有幫助,需要的朋友可以參考下2021-05-05詳解SpringBoot的三種緩存技術(shù)(Spring Cache、Layering Cache 框架、Alibaba J
這篇文章主要介紹了SpringBoot的三種緩存技術(shù),幫助大家更好的理解和學(xué)習(xí)springboot框架,感興趣的朋友可以了解下2020-10-10MyBatis-Plus工具使用之EntityWrapper解析
這篇文章主要介紹了MyBatis-Plus工具使用之EntityWrapper解析,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03