为自引用 class 实例反序列化 json 时出现 StackOverflowException

StackOverflowException when deserializing json for self referencing class instances

我有一个 class 包含 Range[] 作为 属性 和 Range class 是一个自引用 class。我使用 [JsonIgnore] 来防止 WhosebugException 但它仅适用于 Serialize 而不是 Deserialize。我该如何解决这个问题?

using System;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
namespace testoverflow
{
    class Program
    {
        public static void Main(string[] args)
        {
            GlobalVariable.Json = "[{\"TotalBytesReceived\":0,\"Id\":\"b03750fb291a46708f8e1a7409553075\",\"NofThread\":8,\"Speed\":0,\"Progress\":0.0,\"FilePath\":\"C:\\Users\\kafeinaltor\\Downloads\",\"RangeDir\":\"C:\\Users\\kafeinaltor\\AppData\\Roaming\",\"Url\":\"http://ipv4.download.thinkbroadband.com/20MB.zip\",\"Ranges\":[{\"Start\":0,\"End\":9223372036854775806,\"TotalBytesReceived\":0,\"IsDownloaded\":false,\"FileId\":\"87cd7715dc0740c1b82ddd681bf2523d\",\"Size\":9223372036854775807,\"Status\":4,\"IsIdle\":false,\"SaveDir\":\"C:\\Users\\kafeinaltor\\AppData\\Roaming\",\"FilePath\":\"C:\\Users\\kafeinaltor\\AppData\\Roaming\\87cd7715dc0740c1b82ddd681bf2523d\",\"Md5Checksum\":null}],\"Info\":null,\"DownloadRequestMessage\":null}]";
            var a = new MTDO();
            Console.WriteLine(GlobalVariable.Json);
            Console.ReadKey(true);
        }
        public static class GlobalVariable
        {
            public static string Json { get; set; }
        }
        public class MTDO
        {
            public MTDO()
            {
                Ranges = new Range[]
                {
                    new Range(0L, 100L, ""),
                    new Range(101L, 200L, "")
                };
                Id = Guid.NewGuid().ToString("N");
                Reminder.AddOrUpdate(this);
            }
            public string Id { get; set; }
            public Range[] Ranges{ get; set; }
        }
        public class Range
        {
            public long Start { get; set; }
            public long End { get; set; }
            
            public string SaveDir { get; set; }
            public long TotalBytesReceived{ get; set; }
            public Range(long start, long end, string saveDir)
            {
                this.Start = start;
                this.End = end;
                this.SaveDir = Guid.NewGuid().ToString();
                
            }
            [JsonIgnore]
            public Range Remaining
            {
                get
                {
                    return new Range(Start + TotalBytesReceived, End, SaveDir);
                }
            }
        }
        
        public class Reminder
        {
            
            public Reminder()
            {
            }
        
            public static void AddOrUpdate(MTDO mtdo)
            {
            
                var list = JsonConvert.DeserializeObject<List<MTDO>>(Read());
            
                if (list == null)
                    list = new List<MTDO>();
                var exists = list.Any(x => x.Id == mtdo.Id);
                if (!exists)
                    list.Add(mtdo);
                else
                {
                    var i = list.Select((x, j) => new {val = x, index = j})
                    .First(x => x.val.Id == mtdo.Id).index;
                    list[i] = mtdo;
                }
                WriteJson(list);
            
            }
            public static List<MTDO> ReadList()
            {
                var list = JsonConvert.DeserializeObject<List<MTDO>>(Read());
                if (list == null)
                    list = new List<MTDO>();
                return list;
            }
            static string Read()
            {
                try
                {
                    return GlobalVariable.Json;
                }
                catch
                {
                    return "";
                }
            }
            static void WriteJson(List<MTDO> list)
            {
                GlobalVariable.Json = JsonConvert.SerializeObject(list);
            }
        }
    }
}

更新:我更新了我的问题,在控制台应用程序中添加了最少的可重现代码。可以直接copy/paste和运行。

问题是你有一个无限递归:

  1. 您调用 MTDO 构造函数
  2. 在 MTDO 构造函数中调用 Reminder.AddOrUpdate(this);
  3. 在那个方法里面你有 var list = JsonConvert.DeserializeObject<List<MTDO>>(Read());
  4. 再次调用 MTDO 构造函数(第 1 步)

不断重复这些步骤,直到您遇到 WhosebugException。