Сериализация объекта, содержащего значение циклического объекта - PullRequest
137 голосов
/ 21 февраля 2012

У меня есть объект (дерево разбора), который содержит дочерние узлы, которые являются ссылками на другие узлы.

Я хотел бы сериализовать этот объект, используя JSON.stringify(), но я получаю: TypeError: cyclic object value из-за конструкций, которые я упомянул.

Как я мог обойти это?Для меня не имеет значения, представлены ли эти ссылки на другие узлы в сериализованном объекте.

С другой стороны, удаление этих свойств из объекта при их создании кажется утомительным, и я бы не сталхочу внести изменения в парсер (нарцисс).

Ответы [ 5 ]

198 голосов
/ 21 февраля 2012

Используйте второй параметр stringify, функцию замены , чтобы исключить уже сериализованные объекты:

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

результат будет неверным. Если ваша структура подобна этой, лучше использовать вариант Крокфорда decycle .

2 голосов
/ 29 марта 2014

Я создал 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/

1 голос
/ 29 марта 2015

Я также создаю проект 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

Редактировать: IЯ должен преобразовать свой скрипт для NPM https://github.com/bormat/borto_circular_serialize, и я изменил имена функций с французского на английский.

1 голос
/ 08 ноября 2014
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.

1 голос
/ 22 апреля 2013

намного экономит и показывает, где был цикл объект.

<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__"]}]}
...