JSON工具学习记录--FastJSON循环引用问题

JSON工具学习记录–FastJSON循环引用问题

标签(空格分隔): fastjson


最近基于他人项目做二次开发,遇到了循环引用的问题,简单来说A引用了B,B引用了C,C引用了A,那么转换json就会无休止的转换下去.
更复杂的情况,A中引用了B,B中引用了一个A的集合,比如广告引用了广告类型,广告类型里面又有该类型下的所属广告.

1.构造情景

这种又叫做双向引用,个人感觉这种设计本身就不是很合理,当然还要看具体使用场景了.

广告类:

/**
 * @author Niu Li
 * @date 2016/8/12
 */
public class ADEntity {
    private int id;
    private String name;
    //引用了一个广告实体类
    private ADTypeEntity adTypeEntity;

    public ADEntity(int id, String name, ADTypeEntity adTypeEntity) {
        this.id = id;
        this.name = name;
        this.adTypeEntity = adTypeEntity;
    }
//省略get和set
}

广告实体类:

import java.util.List;

/**
 * @author Niu Li
 * @date 2016/8/12
 */
public class ADTypeEntity {
    private int id;
    private String name;
    //引用了其广告集合
    private List lists;
    //省略get和set
}

测试代码:

public class TestApp {
    public static void main(String[] args) {
        //构造广告类型
        ADTypeEntity adTypeEntity = new ADTypeEntity();
        adTypeEntity.setId(1);
        adTypeEntity.setName("轮播图");
        //构造广告
        ADEntity entity1 = new ADEntity(1,"图1",adTypeEntity);
        ADEntity entity2 = new ADEntity(2,"图2",adTypeEntity);
        ADEntity entity3 = new ADEntity(3,"图3",adTypeEntity);

        List lists = new ArrayList();
        lists.add(entity1);
        lists.add(entity2);
        lists.add(entity3);
        //双向引用
        adTypeEntity.setLists(lists);

        String result = JSON.toJSONString(entity1);
        System.out.println(result);
    }
}

结果可以看到双向引用被替换成$ref了:

{
  "adTypeEntity": {
    "id": 1,
    "lists": [
      {
        "$ref": "$"
      },
      {
        "adTypeEntity": {
          "$ref": "$.adTypeEntity"
        },
        "id": 2,
        "name": "图2"
      },
      {
        "adTypeEntity": {
          "$ref": "$.adTypeEntity"
        },
        "id": 3,
        "name": "图3"
      }
    ],
    "name": "轮播图"
  },
  "id": 1,
  "name": "图1"
}

2.解决办法

两种解决办法就是哪里有循环引用,就过滤掉该字段.

1.过滤的方式可以使用JSONField注解声明该字段不被转换为json

    @JSONField(serialize = false)
    private List lists;

得到结果

{
  "adTypeEntity": {
    "id": 1,
    "name": "轮播图"
  },
  "id": 1,
  "name": "图1"
}

2.自定义转换字段

 SimplePropertyPreFilter filter = new SimplePropertyPreFilter(ADTypeEntity.class,"id","name");
        String result = JSON.toJSONString(entity1,filter);

这表明对于ADTypeEntity类只序列化id和name字段,这样的话就排除掉list集合引用了,得到的结果和上面一样.


3.置顶转换规则多级过滤

转载自:http://www.cnblogs.com/sandyfog/articles/3679804.html

fastjson可以实现一些接口指定过滤属性.过滤本质是fastjson每一次要序列化字段的时候都会经过Apply方法进行判断,只有Apply返回true的时候才会序列化,因此就可以根据属性名来进行自由过滤.

以下实现PropertyPreFilter接口,根据name值来进行排除判定.

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.PropertyPreFilter;
import com.alibaba.fastjson.serializer.SerializerFeature;

import java.util.HashMap;
import java.util.Map;

/**
 * @date 2016/8/17
 */
public class ComplexPropertyPreFilter implements PropertyPreFilter {
    private Map, String[]> includes = new HashMap<>();
    private Map, String[]> excludes = new HashMap<>();

    static {
        //全局配置关闭循环引用检测
        JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask();
    }

    public ComplexPropertyPreFilter() {

    }

    public ComplexPropertyPreFilter(Map, String[]> includes) {
        super();
        this.includes = includes;
    }

    public boolean apply(JSONSerializer serializer, Object source, String name) {

        //对象为空。直接放行
        if (source == null) {
            return true;
        }

        // 获取当前需要序列化的对象的类对象
        Class clazz = source.getClass();

        // 无需序列的对象、寻找需要过滤的对象,可以提高查找层级
        // 找到不需要的序列化的类型
        for (Map.Entry, String[]> item : this.excludes.entrySet()) {
            // isAssignableFrom(),用来判断类型间是否有继承关系
            if (item.getKey().isAssignableFrom(clazz)) {
                String[] strs = item.getValue();

                // 该类型下 此 name 值无需序列化
                if (isHave(strs, name)) {
                    return false;
                }
            }
        }

        // 需要序列的对象集合为空 表示 全部需要序列化
        if (this.includes.isEmpty()) {
            return true;
        }

        // 需要序列的对象
        // 找到不需要的序列化的类型
        for (Map.Entry, String[]> item : this.includes.entrySet()) {
            // isAssignableFrom(),用来判断类型间是否有继承关系
            if (item.getKey().isAssignableFrom(clazz)) {
                String[] strs = item.getValue();
                // 该类型下 此 name 值无需序列化
                if (isHave(strs, name)) {
                    return true;
                }
            }
        }

        return false;
    }

    /*
     * 此方法有两个参数,第一个是要查找的字符串数组,第二个是要查找的字符或字符串
     */
    public static boolean isHave(String[] strs, String s) {

        for (int i = 0; i < strs.length; i++) {
            // 循环查找字符串数组中的每个字符串中是否包含所有查找的内容
            if (strs[i].equals(s)) {
                // 查找到了就返回真,不在继续查询
                return true;
            }
        }

        // 没找到返回false
        return false;
    }

    public Map, String[]> getIncludes() {
        return includes;
    }

    public void setIncludes(Map, String[]> includes) {
        this.includes = includes;
    }

    public Map, String[]> getExcludes() {
        return excludes;
    }

    public void setExcludes(Map, String[]> excludes) {
        this.excludes = excludes;
    }
}

自己代码中用到的过滤

 Pageable pageable = new Pageable(page,rows);
        Page promotionPages = promotionService.findPage(pageable);

        String[] promotionFilters = {"id", "name","title","image","endDate","priceExpression","memberRanks"};
        String[] memberFilter = {"id","name"};
        String[] pageFilter = {"pageNumber","pageSize","content","total","totalPages"};
        Map,String[]> mapFilter = new HashMap<>();
        mapFilter.put(Promotion.class,promotionFilters);
        mapFilter.put(MemberRank.class,memberFilter);
        mapFilter.put(Page.class,pageFilter);

        ResultData result = new ResultData.Builder(ResultVo.OK)
                .setData(APPJsonUtil.toJsonObject(mapFilter,null,promotionPages))
                .builder();

json工具

 /**
     * 按照指定序列转换为jsonObject,多级过滤
     * @param object
     * @return
     */
    public static JSONObject toJsonObject(Map, String[]> includes,Map, String[]> excludes, Object object){
        ComplexPropertyPreFilter filter = new ComplexPropertyPreFilter();
        if (excludes != null) {
            filter.setExcludes(excludes);
        }
        if (includes != null) {
            filter.setIncludes(includes);
        }
        String result = JSON.toJSONString(object, filter);
        return JSON.parseObject(result);
    }

你可能感兴趣的:(JSON,javaWEB实战)