引言:記錄最近一次做項目過程中碰到的一個FastJson序列化的問題,本次項目基於spring boot實現,在接口返回數據的時候,實體類的序列化是由FastJson完成的,但是由於功能需要,我需要將某個實體類中的些為空的字段則不返回,但是不能改動FastJson作為序列化的大邏輯,也就是說不能將序列化由FastJson替換為JackSon,但是要實現Jackson中@JsonInclude注解的效果。
【注】FastJson默認是會將沒賦值的屬性不進行序列化,但是本項目中設置了FastJson將空值設為默認值的配置(例如:SerializerFeature.WriteNullNumberAsZero,SerializerFeature.WriteNullStringAsEmpty等),該配置不可更改,如果沒配置,則本文內容就可以略過啦,你寫的代碼沒有bug
解決方案:自定義注解+反射+自定義實現PropertyPreFilter接口實現
1、先簡單介紹一下PropertyPreFilter接口
屬性過濾器:使用PropertyPreFilter過濾屬性
public interface PropertyPreFilter extends SerializeFilter { boolean apply(JSONSerializer serializer, Object object, String name); }
FastJson官方通過SimplePropertyPreFilter實現了該接口,可用於排除一些字段,簡單使用如下
public class TestVo implements Serializable { private static final long serialVersionUID = 4468516188008268414L; private Long id; private Integer age; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
public class TestFastJson { public static void main(String[] args) { TestVo testVo = new TestVo(); testVo.setId(1L); testVo.setName("微雨"); SimplePropertyPreFilter preFilter = new SimplePropertyPreFilter(); preFilter.getExcludes().add("name"); System.out.println(JSONObject.toJSONString(testVo, preFilter, SerializerFeature.WriteNullNumberAsZero, SerializerFeature.WriteNullStringAsEmpty)); } }
返回結果:{"age":0,"id":1}
age返回默認值,name被排除
粗略研究一下SimplePropertyPreFilter接口實現apply()方法的源碼,可以得出以下結論:如果某個字段需要排除,則直接返回false就好了
public boolean apply(JSONSerializer serializer, Object source, String name) { if (source == null) { return true; } if (clazz != null && !clazz.isInstance(source)) { return true; }
// 重點代碼,如果某個字段需要排除,則直接返回false就好了 if (this.excludes.contains(name)) { return false; } if (maxLevel > 0) { int level = 0; SerialContext context = serializer.context; while (context != null) { level++; if (level > maxLevel) { return false; } context = context.parent; } } if (includes.size() == 0 || includes.contains(name)) { return true; } return false; }
2、具體實現
思路:自定義一個注解,當序列化前,通過反射獲取屬性上是否有自定義注解,如果有,則獲取屬性的值,如果值為空則返回false,否則就返回true
自定義注解:
/**
* Description:該注解加在屬性上,如果屬性的值為空,則該屬性不進行序列化返回
* 參考Jackson的@JsonInclude注解
*/
@Target({ElementType.FIELD,ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface CustomizeJsonExclude { }
注入FastJsonHttpMessageConverter對象(controller層或方法如果加了@RestController或@ResponseBody注解,返回JSON數據的時候會自動調用fastjson進行序列化,不配置則默認采用StringHttpMessageConverter)
@Configuration public class FastJsonConfiguration { @Bean public FastJsonHttpMessageConverter fastJsonHttpMessageConverter() { FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); FastJsonConfig fastJsonConfig = new FastJsonConfig(); fastJsonConfig.setSerializerFeatures( //保留map空的字段 | 如果保留的話,map 里面的值如果是null 則會保留null // SerializerFeature.WriteMapNullValue, // 將String類型的NULL轉化為"" SerializerFeature.WriteNullStringAsEmpty, // 將Number類型的NULL轉化為0 SerializerFeature.WriteNullNumberAsZero, // 將List類型的NULL轉成[] SerializerFeature.WriteNullListAsEmpty, // 將Boolean類型的NULL轉化為false SerializerFeature.WriteNullBooleanAsFalse, // 避免循環引用 SerializerFeature.DisableCircularReferenceDetect ); ValueFilter valueFilter = (object, name, value) -> { if (null == value){ value = ""; } return value; }; /** * 關鍵代碼,實現PropertyPreFilter中的apply()方法 * 參數說明: * 1、serializer:不知道有啥用,沒具體研究,有興趣的小伙伴可自行研究 * 2、source:需要序列化的對象 * 3、name:對象中的字段名 */ PropertyPreFilter preFilter = (serializer, source, name) -> { if (source == null) { return true; } Class<?> clazz = source.getClass(); try { Field field = clazz.getDeclaredField(name); if (!field.isAccessible()){ field.setAccessible(true); }
// 判斷字段上有沒有自定義注解CustomizeJsonExclude CustomizeJsonExclude annotation = field.getAnnotation(CustomizeJsonExclude.class); if (Objects.nonNull(annotation)){
// 獲取字段的值,判斷是否為空 Object value = field.get(source); return Objects.nonNull(value); } } catch (Exception e) { e.printStackTrace(); } return true; }; fastJsonConfig.setSerializeFilters(preFilter,valueFilter); converter.setDefaultCharset(Charset.forName("UTF-8")); converter.setFastJsonConfig(fastJsonConfig); List<MediaType> mediaTypeList = new ArrayList<>(); // 解決中文亂碼問題,相當於在Controller上的@RequestMapping中加了個屬性produces = "application/json" mediaTypeList.add(MediaType.APPLICATION_JSON); converter.setSupportedMediaTypes(mediaTypeList); return converter; } }
3、效果
public class FinalSearchResult implements SearchResult, Serializable { private static final long serialVersionUID = -9084386156922106357L; @CustomizeJsonExclude private ProductSearchRepVo productRep; @CustomizeJsonExclude private String priceResult; @CustomizeJsonExclude private String articleResult; public ProductSearchRepVo getProductRep() { return productRep; } public void setProductRep(ProductSearchRepVo productRep) { this.productRep = productRep; } public String getPriceResult() { return priceResult; } public void setPriceResult(String priceResult) { this.priceResult = priceResult; } public String getArticleResult() { return articleResult; } public void setArticleResult(String articleResult) { this.articleResult = articleResult; } }
返回結果:
返回結果達到預期
----------------------------------------------------
附:本次分享內容如果有不正確的地方,歡迎各位留言指正