还原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就会错误。
举个例子:客户与客户联系人。客户有一个客户联系人的集合的属性,客户还有一个主联系人的属性。同时客户联系人也指向客户有一个属性,当这种对应关系的时候解析就会出错!
我尝试着想要去解决,但是智商有限搞不了。求作者在查看一下。
看来这个问题还是有人关注的哈。
你可以给点数据,我有空的时候的看看。
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。