Serialize List<LinkedListNode<object>> using Json.net

萝らか妹 提交于 2021-02-08 12:14:26

问题


example of code with string also throws exception:

LinkedList<string> l = new LinkedList<string>();
        l.AddLast("Kuku");
        l.AddLast("Riku");
        l.AddLast("Ok");
        List<LinkedListNode<string>> lst = new List<LinkedListNode<string>>();
        lst.Add(l.First);
        lst.Add(l.First.Next);
        lst.Add(l.Last);

        string json = JsonConvert.SerializeObject(lst, Formatting.Indented,
       new JsonSerializerSettings
       {
           ReferenceLoopHandling = ReferenceLoopHandling.Serialize
       });
        File.WriteAllText(@"C:\Student Routine\Data.txt", json);`

I can not serialize List<LinkedListNode<object>> due to a self-referral error, using Json.net.

the error:Self referencing loop detected for property 'Previous' with type 'System.Collections.Generic.LinkedListNode`1[Calendar_Module.ScheduleEvent]'. Path '[0].UserData.Calendar.Days.2017-04-02T00:00:00[0].Next.Next.Next.Next.Next.Next'.

please help


回答1:


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.




回答2:


Unhandled Exception:
System.Runtime.Serialization.SerializationException: Type System.Collections.Generic.LinkedListNode`1[System.String] is not marked as Serializable.

In other words, don't use LinkedListNode if you intend to serialize it…



来源:https://stackoverflow.com/questions/43293914/serialize-listlinkedlistnodeobject-using-json-net

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!