后端数据实体都是由hibernate生成的,与浏览器客户端交互json时,采用了alibaba FastJson库。

首先要说fastJson的确为众多json类库中数一数二的,api简单易用,性能强悍,测试完整,典型的国产高端货。

由于后端涉及几个数据库三四百张表,生成的实体之间的嵌套关系也非常复杂,再由fastJson将Bean转化为json string时,一个非常的典型的问题就出现了,就是对象间的嵌套循环引用,如果没有合理的json生成策略,那将是一个无底洞的死循环,直到堆栈溢出。(循环引用的数据不能排除掉因为前端需要读取)

(简单说就是A引用B,B引用C,C引用A,实际上比这更复杂的环形引用链)

fastJson内置有合理的循环引用检测, 采用了比较广泛的json path表示法,避免了反射Bean时循环引用造成的死循环。类似于这样的形式 {"$ref":"$.data[1]"}输出,关键看图fastJson采用循环引用后输出结果!

而这种形式,似乎只有dojo的dojox.json.ref提供了相应的parse支持,其它地方似乎没有找到合适的解析方法。所以在前端依然无法得到相应的数据。

研究了一下fastJson的循环引用表示,然后对前端ExtJs的decode部分进行了重写,于是可以几乎完整的还原原来Java Bean之间嵌套引用关系。

项目前端是ExtJS v3.4所以直接对Ext方法进行覆盖。

String.prototype.startsWith = function (prefix) {
    return prefix && this.length >= prefix.length && this.substring(0, prefix.length) === prefix;
if (!window.JSON)
    JSON = {};
if (typeof JSON.retrocycle !== 'function') {
    JSON.retrocycle = (function () {
        'use strict';
        var t_obj = typeof {}, t_arr = Object.prototype.toString.apply([]) , t_str = typeof "";
        var walk = function (path, _xpath, array) {
            if (path.startsWith('$'))   // 基于xpath直接定位
                return path;
            else {      // 相对回溯定位
                var x , j = path.split('..'), k = -j.length + (array ? 2 : 1), last = j.slice(-1)[0].replace('/', '.');
                x = k < 0 ? _xpath.slice(0, k) : _xpath.slice(0);
                if (last && !last.startsWith('.') && !last.startsWith('['))
                    last = '.' + last;
                path = x.join('.') + last;
            return path;    // 最终得到绝对xpath地址
        return function ($) {
            var xpath = ['$'];
            (function rez(value) {
                var i, item, name, path, _x;
                if (value && typeof value === t_obj) {
                    if (Object.prototype.toString.apply(value) === t_arr) {
                        for (i = 0; i < value.length; i += 1) {
                            item = value[i];
                            if (item && typeof item === t_obj) {
                                xpath.push(xpath.pop() + '[' + i + ']');    // 下标引用要合并分级
                                path = item.$ref;
                                if (typeof path === t_str)
                                    value[i] = eval(walk(path, xpath, true));
                                    rez(item);
                                if (_x = xpath.pop())
                                    xpath.push(_x.slice(0, _x.indexOf('[')));   // 下标引用还原分级
                    } else {
                        for (name in value) {
                            if (value.hasOwnProperty(name) && typeof value[name] === t_obj) {
                                xpath.push(name);
                                item = value[name];
                                if (item) {
                                    path = item.$ref;
                                    if (typeof path === t_str)
                                        value[name] = eval(walk(path, xpath));
                                        rez(item);
                                xpath.pop();
            })($);
            return $;
    })();
Ext.onReady(function () {
    Ext.decode = function () {
        var isNative = function () {
            var useNative = null;
            return function () {
                if (useNative === null) {
                    useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
                return useNative;
        var dc,
            doDecode = function (json) {
                return json ? eval("(" + json + ")") : "";
        return function (json) {
            if (!dc) {
                dc = isNative() ? JSON.parse : doDecode;
//            return dc(json);
            return JSON.retrocycle(dc(json));
    Ext.apply(Ext.util.JSON, {
        decode: Ext.decode

通过覆盖以上方法,便可以还原到原java Bean的嵌套引用关系。

透过console观察一下json解析后并作了复原循环引用后的对象属性,如图:

可能有人担心性能问题,简单的用两个例子测试了一下,跑Ext.decode() 100遍的结果:

{"$ref":".."} // 引用父对象 {"$ref":"../.."} // 引用父对象的父对象 {"$ref":"$.members[0].reportTo"} // 基于路径的引用 (2013/08/02 15:43) @gohsy : 谢谢的你支持。使用好了并参与其中,才是更好的使用开源方式。也就是所谓的社区能读能改。我打算开一个项目用javascript实现fastjson的引用解析,希望你能够参与其中。

fastjson循环引用的文档:

https://github.com/alibaba/fastjson/wiki/%E5%BE%AA%E7%8E%AF%E5%BC%95%E7%94%A8

评论 (0)