zoukankan      html  css  js  c++  java
  • 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;
        }
    }

    返回结果:

     

     返回结果达到预期

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

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

  • 相关阅读:
    非关系数据库之mongodb入门到实战(8)mongodb的常用命令
    非关系数据库之mongodb入门到实战(7)mongodb的window版安装与使用详解
    非关系数据库之mongodb入门到实战(6)mongodb基础详解
    非关系数据库之redis入门到实战(5)Redis企业运用
    非关系数据库之redis入门到实战(4)Redis基本操作
    非关系数据库之redis入门到实战(3)Redis入门介绍与安装
    非关系数据库之redis入门到实战(2)Redis常用命令
    非关系数据库之redis入门到实战(1)Redis高级应用
    Java从入门到实战之(16)面向对象之对象的多态性(四)
    Java从入门到实战之(15)面向对象之接口(三)
  • 原文地址:https://www.cnblogs.com/remote/p/15038751.html
Copyright © 2011-2022 走看看