字符串化(转换为 JSON)一个带有循环引用的 JavaScript 对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10392293/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Stringify (convert to JSON) a JavaScript object with circular reference
提问by user1012500
I've got a JavaScript object definition which contains a circular reference: it has a property that references the parent object.
我有一个 JavaScript 对象定义,它包含一个循环引用:它有一个引用父对象的属性。
It also has functions that I don't want to be passed through to the server. How would I serialize and deserialize these objects?
它还具有我不想传递到服务器的功能。我将如何序列化和反序列化这些对象?
I've read that the best method to do this is to use Douglas Crockford's stringify. However, I'm getting the following error in Chrome:
我读过最好的方法是使用 Douglas Crockford 的 stringify。但是,我在 Chrome 中收到以下错误:
TypeError: Converting circular structure to JSON
类型错误:将圆形结构转换为 JSON
The code:
编码:
function finger(xid, xparent){
this.id = xid;
this.xparent;
//other attributes
}
function arm(xid, xparent){
this.id = xid;
this.parent = xparent;
this.fingers = [];
//other attributes
this.moveArm = function() {
//moveArm function details - not included in this testcase
alert("moveArm Executed");
}
}
function person(xid, xparent, xname){
this.id = xid;
this.parent = xparent;
this.name = xname
this.arms = []
this.createArms = function () {
this.arms[this.arms.length] = new arm(this.id, this);
}
}
function group(xid, xparent){
this.id = xid;
this.parent = xparent;
this.people = [];
that = this;
this.createPerson = function () {
this.people[this.people.length] = new person(this.people.length, this, "someName");
//other commands
}
this.saveGroup = function () {
alert(JSON.stringify(that.people));
}
}
This is a test case that I created for this question. There are errors within this code but essentially I have objects within objects, and a reference passed to each object to show what the parent object is when the object is created. Each object also contains functions, which I don't want stringified. I just want the properties such as the Person.Name
.
这是我为此问题创建的测试用例。这段代码中有错误,但本质上我在对象中有对象,并且传递给每个对象的引用以显示创建对象时父对象是什么。每个对象还包含函数,我不想对其进行字符串化。我只想要诸如Person.Name
.
How do I serialize before sending to the server and deserialize it assuming that the same JSON is passed back?
假设传递回相同的 JSON,如何在发送到服务器之前进行序列化并反序列化它?
回答by tocker
Circular structureerror occurs when you have a property of the object which is the object itself directly (a -> a
) or indirectly (a -> b -> a
).
当您拥有对象的属性即对象本身直接 ( a -> a
) 或间接 ( a -> b -> a
)时,会发生圆形结构错误。
To avoid the error message, tell JSON.stringify what to do when it encounters a circular reference. For example, if you have a person pointing to another person ("parent"), which may (or may not) point to the original person, do the following:
为避免错误消息,请告诉 JSON.stringify 在遇到循环引用时要执行的操作。例如,如果您有一个人指向另一个人(“父母”),该人可能(也可能不)指向原来的人,请执行以下操作:
JSON.stringify( that.person, function( key, value) {
if( key == 'parent') { return value.id;}
else {return value;}
})
The second parameter to stringify
is a filter function. Here it simply converts the referred object to its ID, but you are free to do whatever you like to break the circular reference.
to的第二个参数stringify
是一个过滤函数。在这里,它只是将引用的对象转换为其 ID,但您可以随意破坏循环引用。
You can test the above code with the following:
您可以使用以下代码测试上述代码:
function Person( params) {
this.id = params['id'];
this.name = params['name'];
this.father = null;
this.fingers = [];
// etc.
}
var me = new Person({ id: 1, name: 'Luke'});
var him = new Person( { id:2, name: 'Darth Vader'});
me.father = him;
JSON.stringify(me); // so far so good
him.father = me; // time travel assumed :-)
JSON.stringify(me); // "TypeError: Converting circular structure to JSON"
// But this should do the job:
JSON.stringify(me, function( key, value) {
if(key == 'father') {
return value.id;
} else {
return value;
};
});
BTW, I'd choose a different attribute name to "parent
" since it is a reserved word in many languages (and in DOM). This tends to cause confusion down the road...
顺便说一句,我会为“ parent
”选择一个不同的属性名称,因为它是许多语言(以及 DOM)中的保留字。这往往会导致混乱......
回答by Brandon Boone
It appears that dojocan represent circular references in JSON in the form : {"id":"1","me":{"$ref":"1"}}
看来dojo可以以 JSON 的形式表示循环引用:{"id":"1","me":{"$ref":"1"}}
Here is an example:
下面是一个例子:
require(["dojox/json/ref"], function(){
var me = {
name:"Kris",
father:{name:"Bill"},
mother:{name:"Karen"}
};
me.father.wife = me.mother;
var jsonMe = dojox.json.ref.toJson(me); // serialize me
alert(jsonMe);
});?
Produces:
产生:
{
"name":"Kris",
"father":{
"name":"Bill",
"wife":{
"name":"Karen"
}
},
"mother":{
"$ref":"#father.wife"
}
}
Note: You can also de-serialize these circular referenced objects using the dojox.json.ref.fromJson
method.
注意:您还可以使用dojox.json.ref.fromJson
方法反序列化这些循环引用的对象。
Other Resources:
其他资源:
How to serialize DOM node to JSON even if there are circular references?
回答by nevf
I found two suitable modules to handle circular references in JSON.
我找到了两个合适的模块来处理 JSON 中的循环引用。
- CircularJSON https://github.com/WebReflection/circular-jsonwhose output can be used as input to .parse(). It also works in Browsers & Node.js Also see: http://webreflection.blogspot.com.au/2013/03/solving-cycles-recursions-and-circulars.html
- Isaacs json-stringify-safe https://github.com/isaacs/json-stringify-safewhich maybe more readable but can't be used for .parse and is only available for Node.js
- CircularJSON https://github.com/WebReflection/circular-json,其输出可用作 .parse() 的输入。它也适用于浏览器和 Node.js 另请参阅:http: //webreflection.blogspot.com.au/2013/03/solving-cycles-recursions-and-circulars.html
- Isaacs json-stringify-safe https://github.com/isaacs/json-stringify-safe这可能更具可读性,但不能用于 .parse 并且仅适用于 Node.js
Either of these should meet your needs.
其中任何一个都应该满足您的需求。
回答by Matt Evans
Happened upon this thread because I needed to log complex objects to a page, since remote debugging wasn't possible in my particular situation. Found Douglas Crockford's (inceptor of JSON) own cycle.js, which annotates circular references as strings such that they can be reconnected after parsing. The de-cycled deep copy is safe to pass through JSON.stringify. Enjoy!
发生在这个线程上是因为我需要将复杂的对象记录到页面,因为在我的特定情况下无法进行远程调试。找到了 Douglas Crockford(JSON 的接受者)自己的 cycle.js,它将循环引用注释为字符串,以便它们在解析后可以重新连接。去循环的深拷贝可以安全地通过 JSON.stringify。享受!
https://github.com/douglascrockford/JSON-js
https://github.com/douglascrockford/JSON-js
cycle.js: This file contains two functions, JSON.decycle and JSON.retrocycle, which make it possible to encode cyclical structures and dags in JSON, and to then recover them. This is a capability that is not provided by ES5. JSONPath is used to represent the links.
cycle.js:这个文件包含两个函数,JSON.decycle 和 JSON.retrocycle,这使得可以在 JSON 中编码循环结构和 dag,然后恢复它们。这是 ES5 没有提供的能力。JSONPath 用于表示链接。
回答by Kamil Kie?czewski
No-lib
无库
Use below replacer to generate json with string references to duplicate/circular referenced objects
使用下面的替换器生成带有对重复/循环引用对象的字符串引用的 json
let s = JSON.stringify(obj, refReplacer());
function refReplacer() {
let m = new Map();
let v = new Map();
replacer = (field,value) => value;
return function(field, value) {
let p= m.get(this) + (Array.isArray(this) ? `[${field}]` : '.' + field);
let isComplex= value===Object(value)
if (isComplex) m.set(value, p);
let pp = v.get(value)||'';
let path = p.replace(/undefined\.\.?/,'');
let val = pp ? `#REF:${pp[0]=='[' ? '$':'$.'}${pp}` : value;
if(!pp && isComplex) v.set(value, path);
return replacer.call(this, field, val)
}
}
// ---------------
// TEST
// ---------------
// gen obj with duplicate references
let a = { a1: 1, a2: 2 };
let b = { b1: 3, b2: "4" };
let obj = { o1: { o2: a }, b, a }; // duplicate reference
a.a3 = [1,2,b]; // circular reference
b.b3 = a; // circular reference
let s = JSON.stringify(obj, refReplacer(), 4);
console.log(s);
And following parser function to regenerate object for such "ref-json"
并遵循解析器函数为此类“ref-json”重新生成对象
function parseRefJSON(json) {
let objToPath = new Map();
let pathToObj = new Map();
let o = JSON.parse(json);
let traverse = (parent, field) => {
let obj = parent;
let path = '#REF:$';
if (field !== undefined) {
obj = parent[field];
path = objToPath.get(parent) + (Array.isArray(parent) ? `[${field}]` : `${field?'.'+field:''}`);
}
objToPath.set(obj, path);
pathToObj.set(path, obj);
let ref = pathToObj.get(obj);
if (ref) parent[field] = ref;
for (let f in obj) if (obj === Object(obj)) traverse(obj, f);
}
traverse(o);
return o;
}
// ------------
// TEST
// ------------
let s = `{
"o1": {
"o2": {
"a1": 1,
"a2": 2,
"a3": [
1,
2,
{
"b1": 3,
"b2": "4",
"b3": "#REF:$.o1.o2"
}
]
}
},
"b": "#REF:$.o1.o2.a3[2]",
"a": "#REF:$.o1.o2"
}`;
console.log('Open Chrome console to see nested fields:');
let obj = parseRefJSON(s);
console.log(obj);
回答by Timmerz
I used the following to eliminate the circular references:
我使用以下方法来消除循环引用:
JS.dropClasses = function(o) {
for (var p in o) {
if (o[p] instanceof jQuery || o[p] instanceof HTMLElement) {
o[p] = null;
}
else if (typeof o[p] == 'object' )
JS.dropClasses(o[p]);
}
};
JSON.stringify(JS.dropClasses(e));