使用循环引用对JavaScript对象进行Stringify(转换为JSON)


78

我有一个包含循环引用的JavaScript对象定义:它具有引用父对象的属性。

它还具有一些我不想传递给服务器的功能。我将如何序列化和反序列化这些对象?

我读过,做到这一点的最佳方法是使用道格拉斯·克罗克福德的stringify。但是,我在Chrome中遇到以下错误:

TypeError:将圆形结构转换为JSON

编码:

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));
    }
}

这是我为这个问题创建的测试用例。这段代码中有错误,但是本质上我在对象中有对象,并且传递给每个对象的引用显示了创建对象时父对象是什么。每个对象还包含函数,我不想对其进行字符串化。我只想要的属性Person.Name

假设传递相同的JSON,如何在发送到服务器之前进行序列化和反序列化?



Answers:


113

当您拥有对象的属性时,如果对象本身是直接(a -> a)或间接(a -> b -> a)的属性,则会发生圆形结构错误。

为避免错误消息,请告诉JSON.stringify遇到循环引用时该怎么办。例如,如果您有一个人指向另一个人(“父母”),它可能(也可能不)指向原始人,请执行以下操作:

JSON.stringify( that.person, function( key, value) {
  if( key == 'parent') { return value.id;}
  else {return value;}
})

to的第二个参数stringify过滤器函数。在这里,它只是将引用的对象转换为其ID,但是您可以随意做任何破坏循环引用的操作。

您可以使用以下代码测试以上代码:

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;
  };
});

顺便说一句,我将为“ parent”选择一个不同的属性名称,因为它是许多语言(和DOM)中的保留字。这往往会引起混乱。


我们应该parent用什么来改变变量?
Esqarrouth

owner通常使用@Esqarrouth代替parent
DJDaveMark '19

10

看来dojo可以用JSON形式表示循环引用:{"id":"1","me":{"$ref":"1"}}

这是一个例子:

http://jsfiddle.net/dumeG/

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);
});​

产生:

{
   "name":"Kris",
   "father":{
     "name":"Bill",
     "wife":{
          "name":"Karen"
      }
   },
   "mother":{
     "$ref":"#father.wife"
   }
}

注意:您也可以使用dojox.json.ref.fromJson方法反序列化这些循环引用的对象。

其他资源:

即使有循环引用,如何将DOM节点序列化为JSON?

JSON.stringify无法表示循环引用


嗨,谢谢您的回答。我应该说我正在使用jquery作为我的is库。我当时认为不相关。请更新我的帖子。
user1012500

@ user1012500-Dojo与jQuery配合良好。我经常包括其他库或框架,以弥补我的主要框架中的不足。您甚至可以提取toJsonfromJson方法,并围绕它们创建自己的jQuery包装器。这样,您就不需要引入整个框架。不幸的是,jQuery没有开箱即用的功能,并且JSON.stringify无法处理这些类型的对象。因此,除了以上示例之外,您可能还必须自己编写此功能。
布兰登·布恩

1
嗨,布兰登,我很想添加另一个库来解决该问题,因为它增加了该站点的足迹。但是,我试了试一下道场,并试图用你的榜样对付我。但是,我遇到了循环参考问题(我对dojo并不了解,所以我只是尝试了几件事,但主要是基于您的示例):jsfiddle.net/Af3d6/1
user1012500

1
请勿与JavaScript对象文字混淆的JSON不能包含函数,因为它是一种数据交换格式(如XML)。因此,为了序列化为JSON格式,您需要一个兼容的对象文字。本示例将您的示例修改为兼容的(尽管由于您不再使用对象实例,所以可能不是您要找的示例)。即使删除toJSON函数,对基础原型函数的引用仍然使其无效。
布兰登·布恩

1
Dojo的ref模块像微风一样设法对我的模型进行字符串化/解析。在我的场景中,来自crockford的cycle.js库未成功,因为我使用的是newtonsoft json.net来序列化C#对象,并且它不使用jsonpath(与cycle.js一样)。看起来好老道场救了我的一天!感谢您的回答。
JoaoPauloPaschoal

5

我找到了两个合适的模块来处理JSON中的循环引用。

  1. CircularJSON https://github.com/WebReflection/circular-json,其输出可用作.parse()的输入。它也可以在Browsers&Node.js中使用。另请参见:http : //webreflection.blogspot.com.au/2013/03/solving-cycles-recursions-and-circulars.html
  2. Isaacs json-stringify-safe https://github.com/isaacs/json-stringify-safe,可能更具可读性,但不能用于.parse,仅适用于Node.js

这些都应满足您的需求。


4

之所以发生此线程,是因为我需要将复杂的对象记录到页面上,因为在我的特定情况下无法进行远程调试。找到道格拉斯·克罗克福德(Douglas Crockford)(JSON的接收者)自己的cycle.js,该文件将循环引用注释为字符串,以便可以在解析后重新连接它们。循环使用的深层副本可以安全地通过JSON.stringify传递。请享用!

https://github.com/douglascrockford/JSON-js

cycle.js:此文件包含两个函数JSON.decycle和JSON.retrocycle,这使得可以在JSON中编码循环结构和dag,然后对其进行恢复。ES5不提供此功能。JSONPath用于表示链接。


3

无礼

使用下面的replacer生成带有字符串引用的json(类似json-path)以复制/循环引用对象

let s = JSON.stringify(obj, refReplacer());

并遵循解析器功能以从此类“ ref-json”重新生成对象


-13

我使用以下方法消除了循环引用:

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));

3
这将删除jQuery和的实例,而HTMLElement不是循环引用?
ZachB '16

1
@ZachB我认为在他的设置中,这些对他来说是循环的……但是问题是,仅仅因为javascript是使用的语言,并不意味着我们拥有jquery甚至HTML元素。
moeiscool
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.