序列化包含循环对象值的对象

时间:2012-02-21 17:27:29

标签: javascript json jsonserializer stringify

我有一个对象(解析树),它包含对其他节点的引用的子节点。

我想使用JSON.stringify()序列化此对象,但由于我提到的构造,我得到:TypeError: cyclic object value

我怎么能解决这个问题?在序列化对象中是否表示了对其他节点的这些引用并不重要。

另一方面,在创建对象时从对象中删除这些属性似乎很乏味,我不想对解析器(narcissus)进行更改。

8 个答案:

答案 0 :(得分:195)

使用stringify的第二个参数replacer function来排除已经序列化的对象:

var seen = [];

JSON.stringify(obj, function(key, val) {
   if (val != null && typeof val == "object") {
        if (seen.indexOf(val) >= 0) {
            return;
        }
        seen.push(val);
    }
    return val;
});

http://jsfiddle.net/mH6cJ/38/

正如其他评论中正确指出的那样,此代码删除了每个“看到”的对象,而不仅仅是“递归”对象。

例如,对于:

a = {x:1};
obj = [a, a];

结果不正确。如果你的结构是这样的,那么Crockford的decycle是一个更好的选择。

答案 1 :(得分:3)

这是一种替代答案,但是由于很多人来这里调试它们的圆形对象,而且如果没有大量代码的话,这实际上不是一种很棒的方法。 / p>

console.table()是尚不知名的JSON.stringify()功能。只需调用console.table(whatever);,它就会以表格格式将变量记录在控制台中,从而使读取变量的内容变得相当容易和方便。

答案 2 :(得分:2)

我创建了一个GitHub Gist,它能够检测循环结构并对它们进行去除和编码:https://gist.github.com/Hoff97/9842228

转换只需使用JSONE.stringify / JSONE.parse。 它还解码和编码功能。如果要禁用此功能,只需删除第32-48行和第61-85行。

var strg = JSONE.stringify(cyclicObject);
var cycObject = JSONE.parse(strg);

你可以在这里找到一个例子:

http://jsfiddle.net/hoff97/7UYd4/

答案 3 :(得分:1)

更安全,它显示循环对象的位置。

<script>
var jsonify=function(o){
    var seen=[];
    var jso=JSON.stringify(o, function(k,v){
        if (typeof v =='object') {
            if ( !seen.indexOf(v) ) { return '__cycle__'; }
            seen.push(v);
        } return v;
    });
    return jso;
};
var obj={
    g:{
        d:[2,5],
        j:2
    },
    e:10
};
obj.someloopshere = [
    obj.g,
    obj,
    { a: [ obj.e, obj ] }
];
console.log('jsonify=',jsonify(obj));
</script>

产生

jsonify = {"g":{"d":[2,5],"j":2},"e":10,"someloopshere":[{"d":[2,5],"j":2},"__cycle__",{"a":[10,"__cycle__"]}]}

答案 4 :(得分:1)

function stringifyObject ( obj ) {
  if ( _.isArray( obj ) || !_.isObject( obj ) ) {
    return obj.toString()
  }
  var seen = [];
  return JSON.stringify(
    obj,
    function( key, val ) {
      if (val != null && typeof val == "object") {
        if ( seen.indexOf( val ) >= 0 )
          return
          seen.push( val )
          }
      return val
    }
  );
}

缺少前提条件,否则数组对象中的整数值被截断,即[[08.11.2014 12:30:13,1095]] 1095减少到095。

答案 5 :(得分:0)

我创建了一个github项目,它可以序列化循环对象并恢复该类,如果你将它保存在serializename属性中,如String

var d={}
var a = {b:25,c:6,enfant:d};
d.papa=a;
var b = serializeObjet(a);
assert.equal(  b, "{0:{b:25,c:6,enfant:'tab[1]'},1:{papa:'tab[0]'}}" );
var retCaseDep = parseChaine(b)
assert.equal(  retCaseDep.b, 25 );
assert.equal(  retCaseDep.enfant.papa, retCaseDep );

https://github.com/bormat/serializeStringifyParseCyclicObject

编辑:     我已将我的脚本转换为NPM https://github.com/bormat/borto_circular_serialize,并且我将函数名称从法语更改为英语。

答案 6 :(得分:0)

假设您要做要保留循环引用(即用非循环编码来编码循环数据结构),这当然比仅删除或忽略它们要耗费更多精力:

道格拉斯·克罗克福德的答案:cylce.js

我对该方法的(性能)改进:my answer to related SO question

答案 7 :(得分:0)

nodejs 模块 serialijse 提供了一种很好的方法来处理包含循环或 javascript 类实例的任何类型的 JSON 对象。

const { serialize, deserialize } = require("serialijse");


    var Mary = { name: "Mary", friends: [] };
    var Bob = { name: "Bob", friends: [] };

    Mary.friends.push(Bob);
    Bob.friends.push(Mary);

    var group = [ Mary, Bob];
    console.log(group);

    // testing serialization using  JSON.stringify/JSON.parse
    try {
        var jstr = JSON.stringify(group);
        var jo = JSON.parse(jstr);
        console.log(jo);

    } catch (err) {
        console.log(" JSON has failed to manage object with cyclic deps");
        console.log("  and has generated the following error message", err.message);
    }

    // now testing serialization using serialijse  serialize/deserialize
    var str = serialize(group);
    var so = deserialize(str);
    console.log(" However Serialijse knows to manage object with cyclic deps !");
    console.log(so);
    assert(so[0].friends[0] == so[1]); // Mary's friend is Bob

这个序列化器支持

  • 在对象定义中循环
  • 重建类的实例
  • 支持类型化数组、映射和集合
  • 能够在序列化过程中过滤要跳过的属性。
  • Typed Array 的二进制编码(Float32Array 等...)以提高性能。