1. 程式人生 > >JSON工具學習記錄--FastJSON迴圈引用問題

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<ADEntity> 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<ADEntity> lists = new ArrayList<ADEntity>(); 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<ADEntity> 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.置頂轉換規則多級過濾

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<Class<?>, String[]> includes = new HashMap<>();
    private Map<Class<?>, String[]> excludes = new HashMap<>();

    static {
        //全域性配置關閉迴圈引用檢測
        JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask();
    }

    public ComplexPropertyPreFilter() {

    }

    public ComplexPropertyPreFilter(Map<Class<?>, 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<Class<?>, 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<Class<?>, 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<Class<?>, String[]> getIncludes() {
        return includes;
    }

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

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

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

自己程式碼中用到的過濾

 Pageable pageable = new Pageable(page,rows);
        Page<Promotion> 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<Class<?>,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<Class<?>, String[]> includes,Map<Class<?>, 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工具學習記錄--FastJSON迴圈引用問題

JSON工具學習記錄–FastJSON迴圈引用問題 標籤(空格分隔): fastjson 最近基於他人專案做二次開發,遇到了迴圈引用的問題,簡單來說A引用了B,B引用了C,C引用了A,那麼轉換json就會無休止的轉換下去. 更復雜的情況,A中引用了B,

JSON工具學習記錄--FastJSON

最近做專案,總是與json打交道,在用了即可json工具後,個人認為fastJson最好用,很方便,API也清晰可見,所以記錄下使用方法,給需要的人提供幫助.(部分摘抄自網路) 一.API入口 Fastjson API入口類是com.alibaba.fa

centos 下grep工具學習記錄

空白行 標點符號 及其 16進制 查找 快速查找 ... tab 大寫 一、grep工具快速查找表 grep標準正則相關元字符表示及其的含義: 形式 說明 . 這是一個英文的點號。表示匹配任意單個字符; [] 表示匹配指定範圍內的任意單個字符; [^]

Android應用開發------------Json工具類(fastJson和Gson的兩種工具類)

json一般是Android中比較常用的與伺服器互動的資料形式。我們常用的架包來解析json字串有fastjson和gson 分別寫了兩個工具類: fastJson import java.util.ArrayList; import java.util.List;

解決fastjson迴圈引用問題(死迴圈

在雙向對映的一方向中新增“@JSONField(serialize=false)”。 @JSONField(serialize = false) public java.util.Set<CmsDocReceiveFile> getCmsDocRe

java json工具類(FastJson

FastJSON是一個很好的java開源json工具類庫,相比其他同類的json類庫,它的速度的確是fast,最快!但是文件做得不好,在應用前不得不親測一些功能。 實際上其他的json處理工具都和它差不多,api也有幾分相似。 一、JSON規範 參

JSON工具類庫: alibaba/fastjson 使用記錄

-c post lin 序列 tro hive 問題 www. .net JSON工具類庫: alibaba/fastjson 使用記錄 一、了解JSON JSON標準規範中文文檔: http://www.json.org/json-zh.html 最佳實踐:http

Swift學習記錄 -- 14.閉包的使用和解決迴圈引用方法

Swift中的閉包 , 幾乎和OC中的block一模一樣 , 我個人又比較偏好block , 所以覺得閉包還是蠻不錯的 . 在迴圈引用問題上 , 解決方案也更加簡潔 // HttpTool類

學習記錄: 安裝配置自動化工具ansible

ansible學習記錄: 安裝配置ansible更新日期: 2016-11-30系統環境 :centos6.5本機ip :192.168.233.123被管理機ip :192.168.233.124—————————————————————————————————————py版本

[轉] 各種Json解析工具比較 - json-lib/Jackson/Gson/FastJson

config 1.2 content pretty 接口實現 turn sso processor true JSON技術的調研報告一 、各個JSON技術的簡介和優劣1.json-libjson-lib最開始的也是應用最廣泛的json解析工具,json-lib 不好的地方確

Linux 學習記錄:七、fdisk 分區工具

net 硬盤 code sta play 交互 技術 無法使用 編碼表 一、fdisk分區工具 fdisk 是來自 IBM 的老牌分區工具,支持絕大多數操作系統,幾乎所有的 Linux 發行版都裝有 fdisk,包括在 Linux 的 resuce 模式下依然能夠使用。

【PyQt5 學習記錄】005:QMainWindow 及狀態欄、菜單欄和工具

qt5 open file statusbar ati etc con bubuko import 1 #!/usr/bin/env python 2 3 import sys 4 from PyQt5.QtWidgets import (QApplic

小程式學習記錄:讀取discuz生成的json資料

  在discuz後臺資料庫中建表,testjson,並輸入兩條記錄: testjson.php: <?php if(!defined('IN_DISCUZ')) { exit('Access Denied'); } header("Content-Typ

第009講:了不起的分支和迴圈3 | 學習記錄(小甲魚零基礎入門學習Python)

(標答出處: 魚C論壇) 《零基礎入門學習Python》 基礎題: 0、下面的迴圈會列印多少次"I Love FishC"? for i in range(0, 10, 2): print(‘I Love FishC’) (0,2,4,6,8)共5次 1、下面的迴圈會列

第007、008講:了不起的分支迴圈1&2 | 學習記錄(小甲魚零基礎入門學習Python)

(標答出處: 魚C論壇) 《零基礎入門學習Python》 基礎題: if not (money < 100): 上邊這行程式碼相當於? if money >= 100: assert 的作用是什麼? assert斷言是宣告其布林值必須為真的判定,如果發

迴圈引用導致的json序列化失敗

問題 昨天在給系統加日誌後,系統就一直報 Stack Overflow錯誤,找了很久才發現問題,引入的日誌工具使用 gson序列化,而列印的日誌物件裡包含迴圈引用,導致出錯。 簡單復現 /** * C

PyQt5學習記錄(2)---QMainWindow選單欄、狀態列和工具

QMainWindow是繼承自QWidget,提供一個應用程式的主視窗。通過QMainWindow你可以新增自己的QToolBars、QDockWidgets、QMenuBar、QStatusBar等。見下圖: 所以如果我們寫程式的話,主視窗一般都是繼承QMainWind

Json資料的重複引用/迴圈引用($ref)

引用符號 引用 描述 "$ref":".." 上一級 "$ref":"@" 當前物件,也就是自引用 "$ref":"$" 根物件

第007、008講:了不起的分支迴圈1&2 | 學習記錄(小甲魚零基礎入門學習Python)

視訊中小甲魚使用 if elif else 在大多數情況下效率要比全部使用 if 要高,但根據一般的統計規律,一個班的成績一般服從正態分佈,也就是說平均成績一般集中在 70~80 分之間,因此根據統計規律,我們還可以改進下程式以提高效率。 題目備忘:按照100分制,90分以上成績為A,80到90為B,60到

JSON 註解】JSON迴圈引用2----JSON註解@JsonIgnoreProperties+JAVA關鍵字transient+後臺物件與JSON資料的格式互相轉化

接著來說這個JSON迴圈引用的問題: @JsonIgnoreProperties({"hibernateLazyInitializer", "org","roles"})  在實體類上註解,在使用jackjson 來解析就行 參考下面 關於JSON格式的轉化,其實關