SpringMVC Json自定義序列化和反序列化的操作方法
需求背景
需求一:SpringMVC構(gòu)建的微服務(wù)系統(tǒng),數(shù)據(jù)庫(kù)對(duì)日期的存儲(chǔ)是Long類型的時(shí)間戳,前端之前是默認(rèn)使用Long類型時(shí)間,現(xiàn)在前端框架改動(dòng),要求后端響應(yīng)數(shù)據(jù)時(shí),Long類型的時(shí)間自動(dòng)變成標(biāo)準(zhǔn)時(shí)間格式(yyyy-MM-dd HH:mm:ss)。
涉及到這個(gè)轉(zhuǎn)換的范圍挺大,所有的實(shí)體表都有創(chuàng)建時(shí)間createTime和修改時(shí)間updateTime,目前的主要訴求也是針對(duì)這兩個(gè)字段,并且在實(shí)體詳情數(shù)據(jù)和列表數(shù)據(jù)都存在,需要一個(gè)統(tǒng)一的方法,對(duì)這兩個(gè)字段進(jìn)行處理。
需求二:前端請(qǐng)求上傳的JSON報(bào)文,String類型的內(nèi)容,可能會(huì)出現(xiàn)前后有空格的現(xiàn)象,如果前端框架未對(duì)此問題進(jìn)行處理,后端收到的JSON請(qǐng)求反序列化為對(duì)象時(shí),就會(huì)出現(xiàn)String類型的值,前后有空格,現(xiàn)需要一個(gè)統(tǒng)一的處理方法,對(duì)接收的String類型屬性執(zhí)行trim方法。
解決方案
SpringMVC默認(rèn)的JSON框架為jackson,也可以使用fastjson。
jackson框架
自定義序列化
如果項(xiàng)目使用jackson框架做json序列化,推薦的方案是使用@JsonSerialize注解,示例代碼如下:
@JsonSerialize(using = CustomDateSerializer.class) private Long createTime; @JsonSerialize(using = CustomDateSerializer.class) private Long updateTime;
CustomDateSerializer類的實(shí)現(xiàn)示例如下:
public class CustomDateSerializer extends JsonSerializer<Long> { @Override public void serialize(Long aLong, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date date = new Date(aLong); jsonGenerator.writeString(sdf.format(date)); } }
這種方案的好處如下:
- 自定義的實(shí)現(xiàn)類可以復(fù)用
- 精準(zhǔn)到需要轉(zhuǎn)換處理的字段,不受限于createTime和updateTime,更貼近于需求
缺點(diǎn)就是需要轉(zhuǎn)換的字段都需要使用注解,工作量有點(diǎn)大
當(dāng)然有其他的統(tǒng)一處理方案,這里不贅述。
自定義反序列化
在jackson框架上實(shí)現(xiàn)自定義序列化,也是非常方便的,繼承SimpleModule類即可:
@Component public class StringTrimModule extends SimpleModule { public StringTrimModule() { addDeserializer(String.class, new StdScalarDeserializer<String>(String.class) { @Override public String deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException { String value = jsonParser.getValueAsString(); if (StringUtils.isEmpty(value)) { return value; } return value.trim(); } }); } }
fastjson框架
如果工程里出現(xiàn)這個(gè)依賴:
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.62</version> </dependency>
說明此工程使用的json框架為fastjson,那么jackson的@JsonSerialize就不會(huì)有觸發(fā)入口了,我們來看看fastjson的處理方式。
自定義序列化
相應(yīng)的,使用fastjson會(huì)有相應(yīng)的配置類,示例如下:
/** * 統(tǒng)一輸出是采用fastJson * * @return */ @Bean public HttpMessageConverters fastJsonHttpMessageConverters() { //convert轉(zhuǎn)換消息的對(duì)象 FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter(); //處理中文亂碼問題 List<MediaType> fastMediaTypes = new ArrayList<>(); fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8); fastConverter.setSupportedMediaTypes(fastMediaTypes); //是否要格式化返回的json數(shù)據(jù) FastJsonConfig fastJsonConfig = new FastJsonConfig(); fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat); // 添加指定字段的值轉(zhuǎn)換處理 fastJsonConfig.setSerializeFilters(new CustomerDateFilter()); // FastJson禁用autoTypeSupport fastJsonConfig.getParserConfig().setAutoTypeSupport(false); fastConverter.setFastJsonConfig(fastJsonConfig); return new HttpMessageConverters(fastConverter); }
這里需要添加fastjson對(duì)字段值的處理(上述代碼已添加這行代碼),如
// 添加指定字段的值轉(zhuǎn)換處理 fastJsonConfig.setSerializeFilters(new CustomerDateFilter());
CustomerDateFilter為自行實(shí)現(xiàn)的類,代碼如下:
public class CustomerDateFilter implements ValueFilter { @Override public Object process(Object object, String name, Object value) { if (FieldConstants.CREATE_TIME.equalsIgnoreCase(name) || FieldConstants.UPDATE_TIME.equalsIgnoreCase(name)) { // 屬性名為createTime, updateTime進(jìn)行轉(zhuǎn)換處理 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); sdf.setTimeZone(TimeZone.getTimeZone("GMT+8")); if(value instanceof Long) { Long time = (Long) value; Date date = new Date(time); return sdf.format(date); } else { return value; } } return value; } }
這樣就可以把所有響應(yīng)對(duì)象中出現(xiàn)的createTime和updateTime字段統(tǒng)一處理了,無論列表數(shù)據(jù)還是單個(gè)對(duì)象數(shù)據(jù),非常方便。缺點(diǎn)就是除此之外的字段,如果還做不到全系統(tǒng)統(tǒng)一,就需要單獨(dú)處理。
SerializeFilter定制序列化
支持SerializeFilter定制序列化的擴(kuò)展編程接口有以下幾個(gè),可根據(jù)實(shí)際需要進(jìn)行擴(kuò)展:
- PropertyPreFilter: 根據(jù)PropertyName判斷是否序列化;
- PropertyFilter: 根據(jù)PropertyName和PropertyValue來判斷是否序列化;
- NameFilter: 修改Key,如果需要修改Key,process返回值則可;
- ValueFilter: 修改Value;
- BeforeFilter: 序列化時(shí)在最前添加內(nèi)容;
- AfterFilter: 序列化時(shí)在最后添加內(nèi)容;
- 自定義反序列化
fastJson提供了序列化過濾器,來實(shí)現(xiàn)自定義序列化改造,但沒有提供反序列化過濾器,來實(shí)現(xiàn)對(duì)應(yīng)的功能。
方案:@JSONField注解
回到對(duì)JSON報(bào)文String類型的值執(zhí)行trim操作,官網(wǎng)支持@JSONField注解的屬性設(shè)置(要求fastJson版本1.2.36以上):
@JSONField(format="trim") private String name;
在JSON報(bào)文反序列化時(shí),該實(shí)體的name屬性會(huì)自動(dòng)執(zhí)行trim方法進(jìn)行處理。
此方案只有逐個(gè)添加注解,工作量較大。
方案:實(shí)現(xiàn)ObjectDeserializer接口
ObjectDeserializer接口為可以實(shí)現(xiàn)自定義反序列化實(shí)現(xiàn)接口,配合ParserConfig的全局設(shè)置,也可以達(dá)到預(yù)期的效果,合建StringTrimDeserializer類,對(duì)String進(jìn)行處理:
/** * @title: StringTrimDeserializer * @description: 把String類型的內(nèi)容統(tǒng)一做trim操作 */ public class StringTrimDeserializer implements ObjectDeserializer { @Override public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) { // JSON String反序列化的邏輯比較復(fù)雜,在StringCodec的基礎(chǔ)上,對(duì)其結(jié)果調(diào)用trim方法 Object obj = StringCodec.instance.deserialze(parser, type, fieldName); if (obj instanceof String) { String str = (String) obj; return (T) str.trim(); } return (T) obj; } @Override public int getFastMatchToken() { return JSONToken.LITERAL_STRING; } }
相應(yīng)在,在HttpMessageConverters
類fastJsonHttpMessageConverters方法內(nèi)中增加String類的反序列化設(shè)置:
// 設(shè)置String類的全局反序列化規(guī)則:自動(dòng)完成trim操作 ParserConfig.getGlobalInstance().putDeserializer(String.class, new StringTrimDeserializer());
tips:
在StringTrimDeserializer類實(shí)現(xiàn)方法中為什么不直接parser.getLexer().stringVal()
得到值后執(zhí)行trim方法,而是調(diào)用StringCodec.instance的實(shí)現(xiàn)方法?
StringCodec是fastJson默認(rèn)的String類型的反序列化邏輯類,里面要處理的類型有String、StringBuffer、StringBuilder等,還有各種的集合、數(shù)組結(jié)構(gòu),涉及的nextToken值都不相同,總之,對(duì)String文本的反序列化,實(shí)現(xiàn)邏輯和應(yīng)對(duì)的場(chǎng)景都比較復(fù)雜,而此次的需求只是對(duì)String執(zhí)行trim操作,復(fù)雜的邏輯還是交給StringCodec來處理,站在StringCodec的基礎(chǔ)上,對(duì)其結(jié)果執(zhí)行trim方法就可以達(dá)到預(yù)期目標(biāo)。
小結(jié)
今天這篇是記錄Json自定義序列化和反序列化的實(shí)踐方案,開始實(shí)施前先確認(rèn)工程里使用的框架是哪個(gè),否則就會(huì)出現(xiàn)添加了@JsonSerialize注解,搞了大半天沒有效果,回頭一看框架是fastjson,沒有觸發(fā)入口,當(dāng)然得不到預(yù)期效果,小小建議,希望對(duì)你有幫助。
到此這篇關(guān)于SpringMVC Json自定義序列化和反序列化的文章就介紹到這了,更多相關(guān)SpringMVC 序列化和反序列化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)Excel轉(zhuǎn)PDF的兩種方法詳解
使用具將Excel轉(zhuǎn)為PDF的方法有很多,在這里我給大家介紹兩種常用的方法:使用spire轉(zhuǎn)化PDF、使用jacob實(shí)現(xiàn)Excel轉(zhuǎn)PDF,分別應(yīng)對(duì)兩種不一樣的使用場(chǎng)景,需要的可以參考一下2022-01-01SpringBoot封裝響應(yīng)數(shù)據(jù)實(shí)現(xiàn)過程詳解
這篇文章主要介紹了SpringBoot封裝響應(yīng)數(shù)據(jù)實(shí)現(xiàn)過程,SpringBoot響應(yīng)數(shù)據(jù)封裝是指在SpringBoot應(yīng)用程序中,將返回的數(shù)據(jù)進(jìn)行封裝,以便于前端頁(yè)面或其他客戶端使用,感興趣想要詳細(xì)了解可以參考下文2023-05-05Java多線程CyclicBarrier的實(shí)現(xiàn)代碼
CyclicBarrier可以使一定數(shù)量的線程反復(fù)地在柵欄位置處匯集,本文通過實(shí)例代碼介紹下Java多線程CyclicBarrier的相關(guān)知識(shí),感興趣的朋友一起看看吧2022-02-02關(guān)于Spring的統(tǒng)一功能處理(攔截器)實(shí)現(xiàn)
這篇文章主要介紹了關(guān)于Spring的統(tǒng)一功能處理(攔截器)實(shí)現(xiàn),每個(gè)方法中都要單獨(dú)寫用戶登錄驗(yàn)證的方法,即使封裝成公共方法,也一樣要傳參調(diào)用和在方法中進(jìn)行判斷,需要的朋友可以參考下2023-05-05java 對(duì)文件夾目錄進(jìn)行深度遍歷實(shí)例代碼
這篇文章主要介紹了java 對(duì)文件夾目錄進(jìn)行深度遍歷實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-03-03MyBatis動(dòng)態(tài)SQL如何實(shí)現(xiàn)前端指定返回字段
這篇文章主要介紹了MyBatis動(dòng)態(tài)SQL如何實(shí)現(xiàn)前端指定返回字段,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01MyBatis-Plus 查詢指定字段的實(shí)現(xiàn)
這篇文章主要介紹了MyBatis-Plus 查詢指定字段的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12logback?EvaluatorFilter日志過濾器源碼解讀
這篇文章主要為大家介紹了logback?EvaluatorFilter日志過濾器源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11