引言:记录最近一次做项目过程中碰到的一个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; } }
返回结果:
返回结果达到预期
----------------------------------------------------
附:本次分享内容如果有不正确的地方,欢迎各位留言指正