YamlDotNet указать основного родителя для сериализации?

У меня есть класс Planet, содержащий список плиток. У каждой плитки также есть список соседних плиток. Каким-то образом все плитки связаны (косвенно). Теперь я пытаюсь сериализовать это с помощью YamlDotNet. Однако происходит то, что я получаю очень уродливую вложенную сериализацию.

Минимальный рабочий пример: (https://dotnetfiddle.net/sWGKMB)

public class Planet {
    public Tile[] tiles {get;set;}
}

public class Tile {
    public string name { get; set; }
    public Tile[] neighbours { get; set; }
}

public class SerializeObjectGraph
{
    public void Main()
    {
        var p = new Planet();

        var a = new Tile();
        var b = new Tile();
        var c = new Tile();

        a.name = "a";
        b.name = "b";
        c.name = "c";

        a.neighbours = new Tile[] {b,c};
        b.neighbours = new Tile[] {a,c};
        c.neighbours = new Tile[] {b,a};

        p.tiles = new Tile[] {a,b,c};

        var serializer = new Serializer();
        serializer.Serialize(Console.Out, p);
    }
}

Это дает это как документ yaml:

tiles:
- &o0
  name: a
  neighbours:
  - &o1
    name: b
    neighbours:
    - *o0
    - &o2
      name: c
      neighbours:
      - *o1
      - *o0
  - *o2
- *o1
- *o2

Как видите, поскольку все плитки каким-то образом связаны (в моем мини-примере напрямую), первая плитка в списке планеты создаст огромную вложенную структуру для всех плиток.

Можно ли заставить YamlDotNet использовать ссылки в списке "соседей" и фактический класс в списке плиток? Вот так это выглядит:

tiles:
- &o0
  name: a
  neighbours:
  - *o1
  - *o2
- &o1
  name: b
  neighbours:
  - *o0
  - *o2
- &o2
  name: c
  neighbours:
  - *o0
  - *o1

Спасибо!


person The Oddler    schedule 28.08.2016    source источник


Ответы (1)


Из-за того, как реализован сериализатор, невозможно достичь желаемого. Используемая стратегия состоит в том, чтобы испускать объект в первый раз, когда он появляется на графике, и использовать ссылку при каждом другом появлении.

Выполнение того, что вы предлагаете, на самом деле приведет к искажению YAML. Спецификация явно запрещает ссылаться на узел до его объявления:

Для узла псевдонима является ошибкой использовать привязку, которая ранее не встречалась в документе.

Обходной путь - сериализовать отношения соседей отдельно от самих плиток, но вам придется изменить свои объекты или использовать промежуточное представление.

person Antoine Aubry    schedule 31.08.2016
comment
Ах да, я попытался десериализовать свой более приятный yaml, и это не сработало (без ошибок, но пустые ссылки в списке соседей). Не знал, что спецификации явно запрещают это, поэтому я думаю, что не будет другой реализации, которая могла бы это сделать. Печально, но спасибо за информацию: D - person The Oddler; 31.08.2016
comment
На самом деле десериализатор в большинстве случаев поддерживает прямые ссылки. Однако массивы не поддерживаются, потому что при их создании размер неизвестен. Если вы измените тип свойства neighbours на List<Tile>, вы сможете десериализовать свой более приятный yaml. Однако имейте в виду, что это нестандартное поведение. - person Antoine Aubry; 31.08.2016