1

I have a Planet class which contains a list of tiles. Each tile also has a list of neighbouring tiles. In some way all tiles are connected (indirectly). Now I'm trying to serialize this with YamlDotNet. What happens however is that I get a very ugly nested serialization.

A minimal working example: (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);
    }
}

This gives this as yaml document:

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

As you can see, since all tiles are somehow connected (in my mini example directly), the first tile in the planet's list will create a huge nested structure for all tiles.

Is it possible to force YamlDotNet to use the references in the "neighbours" list, and the actual class in the tiles list? So it looks like this:

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

Thanks!

The Oddler
  • 6,314
  • 7
  • 51
  • 94

1 Answers1

1

Because of the way the Serializer is implemented, it is not possible to achieve what you want. The strategy used is to emit the object the first time it appears in the graph, and to use a reference on every other occurrence.

Doing as you suggest would actually produce malformed YAML. The specification explicitly forbids referencing a node before it is declared:

It is an error for an alias node to use an anchor that does not previously occur in the document.

A workaround would be to serialize the neighbor relationships separately from the tiles themselves, but you would have to change your objects, or use an intermediate representation.

Antoine Aubry
  • 12,203
  • 10
  • 45
  • 74
  • Ah yes, I tried deserializing my "nicer" yaml and it didn't work (no errors, but null references in the neighbours list). Didn't know the specifications explicityly forbids this, so I guess there won't be an other implementation that can do this. Sad, but thanks for the info :D – The Oddler Aug 31 '16 at 13:51
  • 1
    Actually the Deserializer supports forward references in most cases. Arrays are not supported though because when they are instantiated, their size is unknown. If you change the type of the `neighbours` property to `List`, you should be able to deserialize your nicer yaml. Keep in mind that this is non-standard behavior, though. – Antoine Aubry Aug 31 '16 at 14:10