从JSON对象中parsing循环引用

如果我有一个来自json.net的序列化JSON,如下所示:

User:{id:1,{Foo{id:1,prop:1}}, FooList{$ref: "1",Foo{id:2,prop:13}} 

我想淘汰赛输出一个foreach over FooList,但我不知道如何继续,因为$ ref的东西可以扔东西。

我想这个解决scheme会强制所有Foos在FooList中渲染,而不使用:

 PreserveReferencesHandling = PreserveReferencesHandling.Objects 

但这似乎是浪费..

您从服务器接收的json对象包含循环引用 。 在使用这个对象之前,你必须首先从对象中删除所有的$ref属性,代替$ref : "1"你必须把这个链接指向的对象。

在你的情况下,它可能是指向ID为1的用户对象

为此,你应该看看github上的Douglas Crockfords Plugin 。有一个cycle.js可以为你做这个工作。

或者你可以使用下面的代码(未经testing):

 function resolveReferences(json) { if (typeof json === 'string') json = JSON.parse(json); var byid = {}, // all objects by id refs = []; // references to objects that could not be resolved json = (function recurse(obj, prop, parent) { if (typeof obj !== 'object' || !obj) // a primitive value return obj; if ("$ref" in obj) { // a reference var ref = obj.$ref; if (ref in byid) return byid[ref]; // else we have to make it lazy: refs.push([parent, prop, ref]); return; } else if ("$id" in obj) { var id = obj.$id; delete obj.$id; if ("$values" in obj) // an array obj = obj.$values.map(recurse); else // a plain object for (var prop in obj) obj[prop] = recurse(obj[prop], prop, obj) byid[id] = obj; } return obj; })(json); // run it! for (var i=0; i<refs.length; i++) { // resolve previously unknown references var ref = refs[i]; ref[0][ref[1]] = byid[refs[2]]; // Notice that this throws if you put in a reference at top-level } return json; } 

让我知道,如果有帮助!

我发现了一些错误和实现的数组支持:

 function resolveReferences(json) { if (typeof json === 'string') json = JSON.parse(json); var byid = {}, // all objects by id refs = []; // references to objects that could not be resolved json = (function recurse(obj, prop, parent) { if (typeof obj !== 'object' || !obj) // a primitive value return obj; if (Object.prototype.toString.call(obj) === '[object Array]') { for (var i = 0; i < obj.length; i++) // check also if the array element is not a primitive value if (typeof obj[i] !== 'object' || !obj[i]) // a primitive value continue; else if ("$ref" in obj[i]) obj[i] = recurse(obj[i], i, obj); else obj[i] = recurse(obj[i], prop, obj); return obj; } if ("$ref" in obj) { // a reference var ref = obj.$ref; if (ref in byid) return byid[ref]; // else we have to make it lazy: refs.push([parent, prop, ref]); return; } else if ("$id" in obj) { var id = obj.$id; delete obj.$id; if ("$values" in obj) // an array obj = obj.$values.map(recurse); else // a plain object for (var prop in obj) obj[prop] = recurse(obj[prop], prop, obj); byid[id] = obj; } return obj; })(json); // run it! for (var i = 0; i < refs.length; i++) { // resolve previously unknown references var ref = refs[i]; ref[0][ref[1]] = byid[ref[2]]; // Notice that this throws if you put in a reference at top-level } return json; } 

我在亚历山大·瓦西里耶夫的回答中纠正了arrays的问题。

我不能评论他的答案(没有足够的声誉点;-)),所以我不得不添加一个新的答案…(我有一个popup式作为最佳实践,不要回答其他答案,只有在原来的问题 – bof)

  if (Object.prototype.toString.call(obj) === '[object Array]') { for (var i = 0; i < obj.length; i++) { // check also if the array element is not a primitive value if (typeof obj[i] !== 'object' || !obj[i]) // a primitive value return obj[i]; if ("$ref" in obj[i]) obj[i] = recurse(obj[i], i, obj); else obj[i] = recurse(obj[i], prop, obj); } return obj; } 

在公认的实现中,如果你正在检查一个数组并且遇到一个原始值,你将返回该值并覆盖该数组。 您想要继续检查数组的所有元素,并在最后返回数组。

 function resolveReferences(json) { if (typeof json === 'string') json = JSON.parse(json); var byid = {}, // all objects by id refs = []; // references to objects that could not be resolved json = (function recurse(obj, prop, parent) { if (typeof obj !== 'object' || !obj) // a primitive value return obj; if (Object.prototype.toString.call(obj) === '[object Array]') { for (var i = 0; i < obj.length; i++) // check also if the array element is not a primitive value if (typeof obj[i] !== 'object' || !obj[i]) // a primitive value continue; else if ("$ref" in obj[i]) obj[i] = recurse(obj[i], i, obj); else obj[i] = recurse(obj[i], prop, obj); return obj; } if ("$ref" in obj) { // a reference var ref = obj.$ref; if (ref in byid) return byid[ref]; // else we have to make it lazy: refs.push([parent, prop, ref]); return; } else if ("$id" in obj) { var id = obj.$id; delete obj.$id; if ("$values" in obj) // an array obj = obj.$values.map(recurse); else // a plain object for (var prop in obj) obj[prop] = recurse(obj[prop], prop, obj); byid[id] = obj; } return obj; })(json); // run it! for (var i = 0; i < refs.length; i++) { // resolve previously unknown references var ref = refs[i]; ref[0][ref[1]] = byid[ref[2]]; // Notice that this throws if you put in a reference at top-level } return json; } 

这实际上是非常简单的,如果你利用JSON.parsereviver参数:

 // example JSON var j = '{"$id":"0","name":"Parent","child":{"$id":"1", "name":"Child","parent":{"$ref":"0"}},"nullValue":null}' function parseAndResolve(json) { var refMap = {}; return JSON.parse(json, function (key, value) { if (key === '$id') { refMap[value] = this; // return undefined so that the property is deleted return void(0); } if (value && value.$ref) { return refMap[value.$ref]; } return value; }); } console.log(parseAndResolve(j)); 

我的解决scheme(适用于数组):

用法:rebuildJsonDotNetObj(jsonDotNetResponse)

代码:

 function rebuildJsonDotNetObj(obj) { var arr = []; buildRefArray(obj, arr); return setReferences(obj, arr) } function buildRefArray(obj, arr) { if (!obj || obj['$ref']) return; var objId = obj['$id']; if (!objId) { obj['$id'] = "x"; return; } var id = parseInt(objId); var array = obj['$values']; if (array && Array.isArray(array)) { arr[id] = array; array.forEach(function (elem) { if (typeof elem === "object") buildRefArray(elem, arr); }); } else { arr[id] = obj; for (var prop in obj) { if (typeof obj[prop] === "object") { buildRefArray(obj[prop], arr); } } } } function setReferences(obj, arrRefs) { if (!obj) return obj; var ref = obj['$ref']; if (ref) return arrRefs[parseInt(ref)]; if (!obj['$id']) //already visited return obj; var array = obj['$values']; if (array && Array.isArray(array)) { for (var i = 0; i < array.length; ++i) array[i] = setReferences(array[i], arrRefs) return array; } for (var prop in obj) if (typeof obj[prop] === "object") obj[prop] = setReferences(obj[prop], arrRefs) delete obj['$id']; return obj; }