【问题标题】:StackOverflowException when deserializing json for self referencing class instances反序列化自引用类实例的 json 时出现 StackOverflowException
【发布时间】:2020-12-01 15:24:06
【问题描述】:

我有一个包含Range[] 作为属性的类,Range 类是一个自引用类。我使用[JsonIgnore] 来阻止StackoverflowException,但它仅适用于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);
            }
        }
    }
}

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

【问题讨论】:

  • 我无法重现此内容,请参阅 dotnetfiddle.net/wopoNO。但是您当前的代码无法编译,您可以edit您的问题分享minimal reproducible example吗?
  • @dbc 我编写了我的最小代码,但它对我来说也很好,我不明白为什么我的应用程序有问题。真正的代码太长了,这里就不分享了。我需要深入研究以找到更多原因。如果我有线索,我会分享。感谢您的努力
  • 你好@dbc。我在 dotnetfiddle.net/qLgGfZ 中模拟我的代码。我在 MTDO 构造函数中调用 AddOrUpdate 函数。这会导致问题吗?

标签: c# json deserialization stack-overflow self-reference


【解决方案1】:

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

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

这些步骤不断重复,直到您得到 StackOverflowException。

【讨论】:

  • 好的,它可以从构造函数中删除代码。但我不明白为什么这会导致递归。我调用了一次反序列化,在构造函数中只有添加操作。在这种情况下应该没有递归。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-11
  • 1970-01-01
  • 2016-11-08
  • 2022-01-04
  • 2014-06-11
  • 1970-01-01
相关资源
最近更新 更多