Вернуться к уроку

Превратите объекты со ссылками в JSON

важность: 3

Превратите объект team из примера ниже в JSON:

var leader = {
  name: "Василий Иванович"
};

var soldier = {
  name: "Петька"
};

// эти объекты ссылаются друг на друга!
leader.soldier = soldier;
soldier.leader = leader;

var team = [leader, soldier];
  1. Может ли это сделать прямой вызов JSON.stringify(team)? Если нет, то почему?
  2. Какой подход вы бы предложили для чтения и восстановления таких объектов?

Ответ на первый вопрос

Обычный вызов JSON.stringify(team) выдаст ошибку, так как объекты leader и soldier внутри структуры ссылаются друг на друга.

Формат JSON не предусматривает средств для хранения ссылок.

Варианты решения

Чтобы превращать такие структуры в JSON, обычно используются два подхода:

  1. Добавить в team свой код toJSON:

    team.toJSON = function() {
      /* свой код, который может создавать копию объекта без круговых ссылок и передавать управление JSON.stringify */
    }

    При этом, конечно, понадобится и своя функция чтения из JSON, которая будет восстанавливать объект, а затем дополнять его круговыми ссылками.

  2. Можно учесть возможную проблему в самой структуре, используя вместо ссылок id. Как правило, это несложно, ведь на сервере у данных тоже есть идентификаторы.

    Изменённая структура может выглядеть так:

    var leader = {
      id: 12,
      name: "Василий Иванович"
    };
    
    var soldier = {
      id: 51,
      name: "Петька"
    };
    
    // поменяли прямую ссылку на ID
    leader.soldierId = 51;
    soldier.leaderId = 12;
    
    var team = {
      12: leader,
      51: soldier
    };

    …Но действительно ли это решение будет оптимальным? Использовать структуру стало сложнее, и вряд ли это изменение стоит делать лишь из-за JSON. Вот если есть другие преимущества, тогда можно подумать.

Универсальный вариант подхода, описанного выше – это использование особой реализации JSON, которая не входит в стандарт и поддерживает расширенный формат для поддержки ссылок.

Она, к примеру, есть во фреймворке Dojo.

При вызове dojox.json.ref.toJson(team) будет создано следующее строковое представление:

[{"name":"Василий Иванович","soldier":{"name":"Петька","leader":{"$ref":"#0"}}},{"$ref":"#0.soldier"}]

Метод разбора такой строки – также свой: dojox.json.ref.fromJson.