还原json循环引用对象的一种办法? 400 报错
后端数据实体都是由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));
else
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));
else
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遍的结果:
| browsers | 用时:ms |
| chrome 28 | 20-30 |
| firefox 22 | 20-35 |
| ie6 | 300-400 |
| ie9 | 23-30 |
目前来看,基本还算满意,不知谁有更好的方法希望也能拿出来分享一下。
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。
既然dojo有,何不把dojo的借鉴一下.######对dojo不是很熟悉,没时间仔细研究。。。######不错,我一直希望有人能够做这个事情,在客户端解析fastjson的应用。######回复 @gohsy : 谢谢的你支持。使用好了并参与其中,才是更好的使用开源方式。也就是所谓的社区能读能改。我打算开一个项目用javascript实现fastjson的引用解析,希望你能够参与其中。######很早就在项目中引入了温少侠的fastjson druid,绝对达到商业软件的水准了,屡用不爽,越用越爽。######
fastjson循环引用的文档:
https://github.com/alibaba/fastjson/wiki/%E5%BE%AA%E7%8E%AF%E5%BC%95%E7%94%A8
######很高端。只是想知道,大部分语言的JSON API应该都不支持循环引用吧,那么循环引用是什么样的需求产生的?可以避免不?Ext.decode = Ext.JSON.decode;
在Extjs 4.2 里的写法。放在与app目录平齐的overrides里面。
然后在APP.js里面加入下面的东西。
Ext.application({
name: 'admin',
extend: 'admin.Application',
requires: [
// 'overrides.grid.RowEditor'
'overrides.JSON'
],
autoCreateViewport: true
});
这个解析的算法还有BUG。就是当A引用B一个集合,A在引用B单个的时候解析出来可能B指向的A就会错误。
举个例子:客户与客户联系人。客户有一个客户联系人的集合的属性,客户还有一个主联系人的属性。同时客户联系人也指向客户有一个属性,当这种对应关系的时候解析就会出错!
我尝试着想要去解决,但是智商有限搞不了。求作者在查看一下。
这个解析的算法还有BUG。就是当A引用B一个集合,A在引用B单个的时候解析出来可能B指向的A就会错误。
举个例子:客户与客户联系人。客户有一个客户联系人的集合的属性,客户还有一个主联系人的属性。同时客户联系人也指向客户有一个属性,当这种对应关系的时候解析就会出错!
我尝试着想要去解决,但是智商有限搞不了。求作者在查看一下。
看来这个问题还是有人关注的哈。
你可以给点数据,我有空的时候的看看。