Stringify (перетворити в JSON) об’єкт JavaScript із круговим посиланням


79

У мене є визначення об’єкта 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 передається назад?



Крокфорд просив би ви використовувати його cycle.js як replacerу вашому stringifyдзвінку, як зазначає @MattEvans , нижче.
jorffin

Відповіді:


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є функція фільтра . Тут він просто перетворює посиланий об'єкт на його ідентифікатор, але ви можете робити все, що завгодно, щоб зламати кругове посилання.

Ви можете протестувати наведений вище код наступним чином:

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?
Ескаррут,

ownerЗазвичай використовується @Esqarrouth parent.
DJDaveMark

10

Здається, доджо може представляти кругові посилання в 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 як свою бібліотеку. Я не вважав, що це було доречно на той час. Оновлю свою публікацію.
user1012500

@ user1012500 - Dojo чудово працює поряд з jQuery. Я часто включаю інші бібліотеки або фреймворки, щоб компенсувати недоліки у своїй основній структурі. Ви навіть можете бути в змозі отримати toJsonі fromJsonметоди і створити свій власний JQuery обгортку навколо них. Таким чином вам не потрібно буде втягувати цілі рамки. На жаль, jQuery не має цієї функції нестандартно, і JSON.stringify не може обробляти такі типи об’єктів. Отже, крім наведених вище прикладів, можливо, вам доведеться кодувати цю функцію самостійно.
Брендон Бун

1
Привіт, Брендоне, я вагаюся додати ще одну бібліотеку для вирішення проблеми, оскільки це додає ще один слід на сайті. Однак я дав додзьо постріл і спробував використати ваш приклад проти мого. Однак я зіткнувся з круговою довідковою проблемою (я не маю знань про доджо, тому я щойно спробував кілька речей, але в основному на основі вашого прикладу): jsfiddle.net/Af3d6/1
user1012500

1
JSON, який не слід плутати з об’єктними літералами JavaScript, не може містити функцій, оскільки це формат обміну даними (наприклад, XML). Отже, для серіалізації до формату JSON потрібен відповідний літерал об’єкта. Цей приклад змінює ваш приклад на відповідність (хоча це може бути не те, що ви шукаєте, оскільки ви більше не працюєте з екземплярами об’єктів). Навіть коли ви видаляєте свої функції toJSON, посилання на базові функції прототипу все одно роблять його недійсним.
Брендон Бун

1
Модулю ref Dojo вдалося стригіфікувати / проаналізувати мої моделі як вітер. У моєму сценарії бібліотека cycle.js з crockford не була успішною, оскільки я використовую newtonsoft json.net для серіалізації об’єктів C #, і він не використовує jsonpath (як використовує cycle.js). Схоже, старий добрий додзе врятував мені день! Дякую за цю відповідь.
JoaoPauloPaschoal

5

Я знайшов два відповідні модулі для обробки кругових посилань у JSON.

  1. CircularJSON https://github.com/WebReflection/circular-json , висновок якого може бути використаний як вхід для .parse (). Він також працює в браузерах і 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

Це сталося з цим потоком, тому що мені потрібно було реєструвати складні об’єкти на сторінці, оскільки віддалена налагодження неможлива у моїй конкретній ситуації. Знайдено власний цикл.js Дугласа Крокфорда (автор JSON), який анотує кругові посилання як рядки, щоб їх можна було повторно підключити після синтаксичного аналізу. Розблоковану глибоку копію безпечно передати через JSON.stringify. Насолоджуйтесь!

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

cycle.js: Цей файл містить дві функції, JSON.decycle та JSON.retrocycle, які дають можливість кодувати циклічні структури та даги в JSON, а потім відновити їх. Це можливість, яка не передбачена ES5. JSONPath використовується для представлення посилань.


3

Ні-lib

Використовуйте замінювач нижче, щоб створити json із посиланнями на рядки (подібний json-шлях ) для дублювання / циклічних об'єктів посилання

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

1
@ZachB, я думаю, що в його налаштуваннях для нього це кругообіг ... але проблема полягає в тому, що те, що JavaScript використовується, не означає, що у нас є jquery або навіть HTML-елементи.
moeiscool
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.