Stringify (конвертировать в JSON) объект JavaScript с круглой ссылкой

У меня есть определение объекта JavaScript, которое содержит циклическую ссылку: у него есть свойство, которое ссылается на родительский объект.

Он также имеет функции, которые я не хочу передавать на сервер. Как бы сериализовать и десериализовать эти объекты?

Я читал, что лучший способ сделать это - использовать Дугласа Крокфорда. Тем не менее, я получаю следующую ошибку в Chrome:

TypeError: Converting circular structure to 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 передан обратно?

Ответ 1

Ошибка круговой структуры возникает, когда у вас есть свойство объекта, непосредственно являющегося объектом (a -> a) или косвенно (a -> b -> a).

Чтобы избежать сообщения об ошибке, сообщите JSON.stringify, что делать, когда он встречает циклическую ссылку. Например, если у вас есть человек, указывающий на другое лицо ( "родитель" ), которое может (или не указывать) указать на оригинальное лицо, выполните следующие действия:

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

Второй параметр 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). Это, как правило, вызывает путаницу в будущем...

Ответ 2

Похоже, что 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 node в JSON, даже если есть круговые ссылки?

JSON.stringify не может представлять круговые ссылки

Ответ 3

Я нашел два подходящих модуля для обработки круговых ссылок в JSON.

Любой из них должен соответствовать вашим потребностям.

Ответ 4

Произошел этот поток, потому что мне нужно было записывать сложные объекты на страницу, поскольку удаленная отладка не была возможна в моей конкретной ситуации. Найден Douglas Crockford (inceptor of JSON) собственный cycle.js, который аннотирует круговые ссылки в виде строк, чтобы их можно было снова подключить после разбора. Децибельная глубокая копия безопасна для прохождения через JSON.stringify. Наслаждайтесь!

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

cycle.js: этот файл содержит две функции: JSON.decycle и JSON.retrocycle, которые позволяют кодировать циклические структуры и проскакивает в JSON, а затем восстанавливает их. Это возможность, которая ES5 не предоставляется. JSONPath используется для представления ссылок.

Ответ 5

Я использовал следующее для исключения круговых ссылок:

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