[Solved] Serialize List using Json.net


Serializing a List<LinkedListNode<string>> is a bit of an odd thing to do – normally one would just serialize the underlying linked list. Perhaps you’re trying to serialize a table that gives the nodes in a different order than the underlying list?

If that’s the case, it might appear that one could serialize the node list using PreserveReferencesHandling.All combined with ReferenceLoopHandling.Serialize, however this fails due to some limitations of Json.NET:

  • PreserveReferencesHandling is not implemented for read-only properties (see here) but LinkedListNode.List, .Next and .Previous are all read-only. This prevents correct serialization of circular dependencies and eventually leads to an infinite recursion on the next and previous node properties.

  • PreserveReferencesHandling is not implemented for objects with non-default constructors (see here) but the only public constructor for LinkedListNode<T> is parameterized.

Thus, you will need to create a custom JsonConverter for your list of nodes:

public class LinkedListNodeListConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(List<LinkedListNode<T>>).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        var list = (existingValue as IList<LinkedListNode<T>> ?? (IList<LinkedListNode<T>>)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator());
        var table = serializer.Deserialize<LinkedListNodeOrderTable<T>>(reader);
        foreach (var node in table.ToNodeList())
            list.Add(node);
        return list;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var list = (IList<LinkedListNode<T>>)value;
        var table = LinkedListNodeOrderTable<T>.FromList(list);
        serializer.Serialize(writer, table);
    }
}

class LinkedListNodeOrderTable<T>
{
    public static LinkedListNodeOrderTable<T> FromList(IList<LinkedListNode<T>> nodeList)
    {
        if (nodeList == null)
            return null;
        try
        {
            var list = nodeList.Where(n => n != null).Select(n => n.List).Distinct().SingleOrDefault();
            var table = new LinkedListNodeOrderTable<T>(list);
            var dictionary = list == null ? null : list.EnumerateNodes().Select((n, i) => new KeyValuePair<LinkedListNode<T>, int>(n, i)).ToDictionary(p => p.Key, p => p.Value);
            table.Indices = nodeList.Select(n => (n == null ? -1 : dictionary[n])).ToList();
            return table;
        }
        catch (Exception ex)
        {
            throw new JsonSerializationException(string.Format("Failed to construct LinkedListNodeOrderTable<{0}>",  typeof(T)), ex);
        }
    }

    public LinkedListNodeOrderTable(LinkedList<T> List)
    {
        this.List = List;
    }

    public LinkedList<T> List { get; set; }

    public List<int> Indices { get; set; }

    public IEnumerable<LinkedListNode<T>> ToNodeList()
    {
        if (Indices == null || Indices.Count < 1)
            return Enumerable.Empty<LinkedListNode<T>>();
        var array = List == null ? null : List.EnumerateNodes().ToArray();
        return Indices.Select(i => (i == -1 ? null : array[i]));
    }
}

public static class LinkedListExtensions
{
    public static IEnumerable<LinkedListNode<T>> EnumerateNodes<T>(this LinkedList<T> list)
    {
        if (list == null)
            yield break;
        for (var node = list.First; node != null; node = node.Next)
            yield return node;
    }
}

And use the following settings:

var settings = new JsonSerializerSettings
{
    Converters = { new LinkedListNodeListConverter<string>() },
};
string json = JsonConvert.SerializeObject(lst, Formatting.Indented, settings);

The resulting JSON will look like:

{
  "List": [
    "Kuku",
    "Riku",
    "Ok"
  ],
  "Indices": [
    0,
    1,
    2
  ]
}

Note that the converter assumes all the nodes in the list are members of the same underlying LinkedList<T>. If not an exception will be thrown.

Sample fiddle.

1

solved Serialize List> using Json.net