【问题标题】:Serialize List<LinkedListNode<object>> using Json.net使用 Json.net 序列化 List<LinkedListNode<object>>
【发布时间】:2017-09-03 18:29:29
【问题描述】:

带有字符串的代码示例也抛出异常:

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);`

由于自我引用错误,我无法使用 Json.net 序列化 List&lt;LinkedListNode&lt;object&gt;&gt;

错误:检测到类型为“System.Collections.Generic.LinkedListNode`1[Calendar_Module.ScheduleEvent]”的属性“Previous”的自引用循环。路径'[0].UserData.Calendar.Days.2017-04-02T00:00:00[0].Next.Next.Next.Next.Next.Next'。

请帮忙

【问题讨论】:

    标签: c# serialization json.net


    【解决方案1】:

    序列化List&lt;LinkedListNode&lt;string&gt;&gt; 有点奇怪——通常只会序列化底层链表。也许您正在尝试序列化一个以与基础列表不同的顺序提供节点的表?

    如果是这种情况,似乎可以使用PreserveReferencesHandling.All 结合ReferenceLoopHandling.Serialize 序列化节点列表,但是由于 Json.NET 的一些限制,这会失败:

    • PreserveReferencesHandling 未针对只读属性实现(请参阅here),但LinkedListNode.List.Next.Previous 都是只读的。这会阻止循环依赖的正确序列化,并最终导致下一个和上一个节点属性的无限递归。

    • PreserveReferencesHandling 不适用于具有非默认构造函数的对象(请参阅here),但LinkedListNode&lt;T&gt; 的唯一公共构造函数是参数化的。

    因此,您需要为您的节点列表创建一个custom JsonConverter

    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;
        }
    }
    

    并使用以下设置:

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

    生成的 JSON 将如下所示:

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

    请注意,转换器假定列表中的所有节点都是同一底层LinkedList&lt;T&gt; 的成员。否则会抛出异常。

    示例fiddle

    【讨论】:

    • 非常感谢,很有帮助
    【解决方案2】:
    Unhandled Exception:
    System.Runtime.Serialization.SerializationException: Type System.Collections.Generic.LinkedListNode`1[System.String] is not marked as Serializable.
    

    换句话说,如果你打算序列化它,不要使用 LinkedListNode...

    【讨论】:

    • 有可能,但是引用有问题。如果你使用 BinarryFormatter 你需要 Serializable 属性
    猜你喜欢
    • 1970-01-01
    • 2013-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多