FastJson简单实现@JsonInclude效果,使得非空字段不返回

引言:记录最近一次做项目过程中碰到的一个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;
    }
}

返回结果:

 

 返回结果达到预期

----------------------------------------------------

附:本次分享内容如果有不正确的地方,欢迎各位留言指正

原文地址:https://www.cnblogs.com/remote/p/15038751.html