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;
    }
}

返回結果:

 

 

 

 返回結果達到預期

 

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

附:本次分享內容如果有不正確的地方,歡迎各位留言指正


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM