StackOverflowException 在 Newtonsoft.JSON.dll 中发生。

huangapple go评论72阅读模式
英文:

StackOverflowException in Newtonsoft.JSON.dll

问题

I have translated the provided content:

我已经在我的 UWP C# 项目中调查了这个异常一段时间了,我正在努力弄清楚到底是什么导致了它。我会感激一个解决方案,甚至是一些关于如何进一步排除故障的提示,超越了我到目前为止所做的工作。

最近,我更新了我的应用程序,使用 Newtonsoft.Json 而不是 System.Text.Json 来序列化 JSON,虽然它能运行,但在稍后的调试会话中(VS 捕获了这个异常,整个应用程序没有崩溃)由于异常而中断了。

异常不会在我启动应用程序后运行 .Save() 时发生。这特别发生在我将一些图像上传到我的应用程序后(我将在下面分享一些代码),然后单击一个调用 ContinueBtn_Click 的按钮,这似乎与我的异常有关。

我一直在仔细查看,但找不到似乎引发无限循环的原因。我在我的代码中设置了断点和输出,确认 .Save() 和 ContinueBtn_Click() 本身只运行一次。

首先,这是错误的屏幕截图:

StackOverflowException 在 Newtonsoft.JSON.dll 中发生。

这是 UserData 类。我在 serializerDefault 中注释掉了 References 行,但即使这些存在,异常也会发生。

以下是我的页面的代码后台方法,它调用 .Save(),ContinueBtn_Click():

// 在用户将一些文件拖放到框中后点击,文件会被处理然后添加到 pendingInit(List
private async void IUInit_ContinueBtn_Click(object sender, RoutedEventArgs e)
{
if (pendingInit.Count > 0)
{
for (int i = 0; i < pendingInit.Count; i++)
{
try
{
App.cmSession.IUSession.workAreaImages.Add(pendingInit[i]);
}
catch (Exception ex)
{
if (App.cmDebugger.debug)
{
Debug.WriteLine("Failed to move pending file to work area: " + ex.Message);
try { Debug.WriteLine("Image: " + pendingInit[i].fileName); } catch { }
}
}
}
IUState = DataState.RefreshNeeded;
App.cmSession.IUSession.sessionActive = true;
// 这似乎是异常发生的地方。
await App.cmData.Save();
await InitializeIU();
}
else
{
Init_Grid.Visibility = Visibility.Collapsed;
}

IUState = DataState.RefreshNeeded;

}

这是在首次调用 .Save() 时成功生成的 JSON:

{
"userSessions": [
{
"LinkedSite": {
"hasContentAccess": true,
"hasMediaAccess": true,
"lastResponse": "(一些 JSON 在此)",
"friendlyName": "网站",
"URL": "http://www.google.com/",
"username": "usr",
"status": "已连接",
"statusDetail": "已连接到网站,没有错误。",
"lastConnectionTime": "2023-05-10T15:49:24.0182548-05:00"
},
"IUSession": {
"sessionActive": false,
"workAreaImages": []
}
}
],
"lastDatabaseConnection": null
}

在冒险使问题变得有点长的风险下,我还提供了在 .Save() 中被序列化的相关数据结构:

CMSession:

public class CMSession
{
public SiteConnection LinkedSite { get; set; }
public ImageUploaderData IUSession { get; set; }

public CMSession(SiteConnection linkedSite)
{
    LinkedSite = new SiteConnection(linkedSite);
    IUSession = new ImageUploaderData();
}

}

DatabaseConnection:

public class DatabaseConnection
{
public string dbName = database;
public string dbstate;
public MySqlConnection connection;
}

SiteConnection:

public class SiteConnection
{
public string friendlyName { get; set; }
public string URL { get; set; }
public string username { get; set; }
public string status { get; set; }
public string statusDetail { get; set; }
public DateTime lastConnectionTime { get; set; }
public bool hasContentAccess;
public bool hasMediaAccess;
public object lastResponse;
}

ImageUploaderData:

public class ImageUploaderData
{
public bool sessionActive { get; set; } = false;
public List workAreaImages { get; set; }
= new List();
}

英文:

I have been investigating this exception for a while in my UWP C# project and I am having trouble figuring out exactly what is causing it. I would appreciate a solution, or even some tips on how to troubleshoot beyond what I have done so far.

I recently updated my app to serialize JSON using Newtonsoft.Json instead of System.Text.Json and while it runs, the Debug session breaks later on (VS catches this one, the whole app doesn't crash) due to the exception.

The exception does not occur when I run .Save() after starting the application. This occurs specifically after I upload some images into my app (I'll share some code below) and then click on a button that calls ContinueBtn_Click, which seems related to my exception.

I have been combing through it, but I can't find anything that seems to cause an endless loop. I set breakpoints and outputs in my code,
confirming that .Save() as well as ContinueBtn_Click() themselves only run once.

To start, here is the screenshot of the error:

StackOverflowException 在 Newtonsoft.JSON.dll 中发生。

Here is the UserData class. I commented out the References lines in serializerDefault, but the exception occurs even if those are present.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-html -->

public class UserData
    {
        public List&lt;CMSession&gt; userSessions { get; set;}
        public DatabaseConnection lastDatabaseConnection { get; set;}
        
        [JsonIgnore]
        JsonSerializerSettings serializerDefaults = new JsonSerializerSettings
        {
            Formatting = Formatting.Indented,
            //PreserveReferencesHandling = PreserveReferencesHandling.All,
            //ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
        };

        public UserData() { userSessions = new List&lt;CMSession&gt;(); }

        /// &lt;summary&gt;
        /// Saves the UserData to a serialized user.json file.
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;devBreak&quot;&gt;If debugging, may provide optional True value to break the debugger prior to save, allowing data manipulation.&lt;/param&gt;
        public async Task Save(bool devBreak = false)
        {
            if (devBreak)
            {
                Debug.WriteLine(&quot;Saving with Break Mode. You may manipulate UserData in Immediate Window now, otherwise Continue.&quot;);
                Debug.Write(&quot;&quot;);
            }
            try
            {
                StorageFile udFile = await ApplicationData.Current.LocalFolder.GetFileAsync(@&quot;UserData\user.json&quot;);
                var udData = JsonConvert.SerializeObject(this, serializerDefaults);
                await Windows.Storage.FileIO.WriteTextAsync(udFile, udData);
                Debug.WriteLine(&quot;User data was manually saved.&quot;);
            } catch(Exception ex)
            {
                if (App.cmDebugger.debug)
                {
                    Debug.WriteLine(&quot;Failed to serialize user data to file: &quot; + ex.Message);
                }
            }
        }
    }

<!-- end snippet -->

Here is my page's code-behind method that is calling .Save(), ContinueBtn_Click():

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-html -->

// Clicked after user drops some files into a box, the files are processed then added to pendingInit (a List&lt;WorkingImage&gt;)
private async void IUInit_ContinueBtn_Click(object sender, RoutedEventArgs e)
        {
            if (pendingInit.Count &gt; 0)
            {
                 for (int i = 0; i &lt; pendingInit.Count; i++)
                {
                    try
                    {
                        App.cmSession.IUSession.workAreaImages.Add(pendingInit[i]);
                    }
                    catch (Exception ex)
                    {
                        if (App.cmDebugger.debug)
                        {
                            Debug.WriteLine(&quot;Failed to move pending file to work area: &quot; + ex.Message);
                            try { Debug.WriteLine(&quot;Image: &quot; + pendingInit[i].fileName); } catch { }
                        }
                    }
                }
                IUState = DataState.RefreshNeeded;
                App.cmSession.IUSession.sessionActive = true;
                // This appears to be where the exception occurs. 
                await App.cmData.Save();
                await InitializeIU();
            }
            else
            {
                Init_Grid.Visibility = Visibility.Collapsed;
            }

            IUState = DataState.RefreshNeeded;
        }

<!-- end snippet -->

Here is the JSON that is being generated successfully the first few times .Save() is called:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-html -->

{
  &quot;userSessions&quot;: [
    {
      &quot;LinkedSite&quot;: {
        &quot;hasContentAccess&quot;: true,
        &quot;hasMediaAccess&quot;: true,
        &quot;lastResponse&quot;: &quot; (some JSON in here)&quot;,
        &quot;friendlyName&quot;: &quot;website&quot;,
        &quot;URL&quot;: &quot;http://www.google.com/&quot;,
        &quot;username&quot;: &quot;usr&quot;,
        &quot;status&quot;: &quot;connected&quot;,
        &quot;statusDetail&quot;: &quot;Connected to the site without errors.&quot;,
        &quot;lastConnectionTime&quot;: &quot;2023-05-10T15:49:24.0182548-05:00&quot;
      },
      &quot;IUSession&quot;: {
        &quot;sessionActive&quot;: false,
        &quot;workAreaImages&quot;: []
      }
    }
  ],
  &quot;lastDatabaseConnection&quot;: null
}

<!-- end snippet -->


At the risk of making the question a bit long, I'm also providing relevant data structures that are serialized in .Save():

CMSession:

<!-- begin snippet: js hide: true console: false babel: false -->

<!-- language: lang-html -->

public class CMSession
    {
        public SiteConnection LinkedSite { get; set; }
        public ImageUploaderData IUSession {  get; set; }

        public CMSession(SiteConnection linkedSite)
        {
            LinkedSite = new SiteConnection(linkedSite);
            IUSession = new ImageUploaderData();
        }
    }

<!-- end snippet -->

DatabaseConnection:

<!-- begin snippet: js hide: true console: false babel: false -->

<!-- language: lang-html -->

    public class DatabaseConnection
    {
        public string dbName = database; 
        public string dbstate;
        public MySqlConnection connection;    
    }

<!-- end snippet -->

SiteConnection:

<!-- begin snippet: js hide: true console: false babel: false -->

<!-- language: lang-html -->

    public class SiteConnection
    {
        public string friendlyName { get; set; }
        public string URL { get; set; }
        public string username { get; set; }
        public string status { get; set; }
        public string statusDetail { get; set; }
        public DateTime lastConnectionTime { get; set; }
        public bool hasContentAccess;
        public bool hasMediaAccess;
        public object lastResponse;
    }

<!-- end snippet -->

ImageUploaderData:

<!-- begin snippet: js hide: true console: false babel: false -->

<!-- language: lang-html -->

    public class ImageUploaderData
    {
        public bool sessionActive {  get; set; } = false;
        public List&lt;WorkingImage&gt; workAreaImages {  get; set; } 
           = new List&lt;WorkingImage&gt;();
    }

<!-- end snippet -->

答案1

得分: 2

不确定百分之百,但我认为你的错误只会在上传图片时出现。我怀疑你正在尝试将图片序列化为JSON。

这也可以通过你的示例JSON中的"workAreaImages": []为空来支持。

用户数据中有一个CMSession的列表

public UserData() { userSessions = new List<CMSession>(); }

CMSession有一个类型为ImageUploaderData的IUSession

public class CMSession
{
    public SiteConnection LinkedSite { get; set; }
    public ImageUploaderData IUSession {  get; set; }

    public CMSession(SiteConnection linkedSite)
    {
        LinkedSite = new SiteConnection(linkedSite);
        IUSession = new ImageUploaderData();
    }
}

ImageUploaderData有一个WorkingImage的列表

public class ImageUploaderData
{
    public bool sessionActive {  get; set; } = false;
    public List<WorkingImage> workAreaImages {  get; set; } 
       = new List<WorkingImage>();
}

所以当你尝试序列化UserData时,当它发现一个图片时会失败。

现在,如果这个WorkingImage确实代表一张图片,那么我不知道Json.Net在序列化过程中如何处理它,我怀疑它在幕后尝试类似于这个问题https://stackoverflow.com/questions/41828014/json-net-stackoverflowexception-while-serialization的操作。

然而,我认为这个答案实际上会帮助你编写一个自定义的JSON序列化器来处理图片https://stackoverflow.com/a/44370397/9822528。

英文:

Not 100% on this, but I think your error is only appearing when you're uploading images is telling. I suspect you're trying to serialize the image to json.

This is also backed by your example json having an empty &quot;workAreaImages&quot;: [] when it's succeeding.

User data has a list of CMSession

    public UserData() { userSessions = new List&lt;CMSession&gt;(); }

CMSession has a IUSession of type ImageUploaderData

public class CMSession
    {
        public SiteConnection LinkedSite { get; set; }
        public ImageUploaderData IUSession {  get; set; }

        public CMSession(SiteConnection linkedSite)
        {
            LinkedSite = new SiteConnection(linkedSite);
            IUSession = new ImageUploaderData();
        }
    }

ImageUploaderData has a list of WorkingImage

public class ImageUploaderData
{
    public bool sessionActive {  get; set; } = false;
    public List&lt;WorkingImage&gt; workAreaImages {  get; set; } 
       = new List&lt;WorkingImage&gt;();
}

So when you try to serialize your UserData, it's failing when it finds an image.

Now if this WorkingImage does represent an image, then I don't know how Json.Net is trying to handle that during serialization, I suspect it's trying to do something similar to in this question https://stackoverflow.com/questions/41828014/json-net-stackoverflowexception-while-serialization behind the scenes

However, I think this answer will actually help you write a custom json serializer for an image https://stackoverflow.com/a/44370397/9822528

答案2

得分: 0

I have identified the issue! Props to @DubDub for pointing me in the direction of "workAreaImages" as a possible issue.

Here is what I did to identify the issue and resolve:

  1. I added a [JsonIgnore] property to all non-primitives in my data objects being serialized. Rebuilt the app and confirmed that it was able to serialize without these suspect objects.
  2. I started removing [JsonIgnore] from various object types, rebuilding the app and testing between each removal until I encountered the problematic object/type, also monitoring my JSON file each time for anything strange/not serializing.
  3. I eventually narrowed it down to a SolidColorBrush. When I removed JsonIgnore from this type, the application would crash.

The object itself wasn't used in my code in a way that should cause a circular reference (it is just a simple reference to a brush), so I am assuming it may be an issue with the type itself (e.g. Newtonsoft.Json doesn't know how to handle this) and will either JsonIgnore it permanently or write a converter/error handler to process it during serialization.

[JsonIgnore]
public SolidColorBrush tagColor;

英文:

I have identified the issue! Props to @DubDub for pointing me in the direction of "workAreaImages" as a possible issue.

Here is what I did to identify the issue and resolve:

  1. I added a [JsonIgnore] property to all non-primitives in my data objects being serialized. Rebuilt the app and confirmed that it was able to serialize without these suspect objects.
  2. I started removing [JsonIgnore] from various object types, rebuilding the app and testing between each removal until I encountered the problematic object/type, also monitoring my JSON file each time for anything strange/not serializing.
  3. I eventually narrowed it down to a SolidColorBrush. When I removed JsonIgnore from this type, the application would crash.

The object itself wasn't used in my code in a way that should cause a circular reference (it is just a simple reference to a brush), so I am assuming it may be an issue with the type itself (e.g. Newtonsoft.Json doesn't know how to handle this) and will either JsonIgnore it permanently or write a converter/error handler to process it during serialization.

[JsonIgnore]
public SolidColorBrush tagColor;

huangapple
  • 本文由 发表于 2023年5月11日 05:46:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/76222757.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定