【问题标题】:StackOverflowException on JsonConvert.DeserializeObjectJsonConvert.DeserializeObject 上的 StackOverflowException
【发布时间】:2017-02-20 04:00:34
【问题描述】:

我正在编写一个解决方案,其中包含一个 ASP.NET Web API (.NET 4.6.2) (即后端)、一个 Web API 客户端实现 PCL(即中间件)和 Xamarin.Forms 项目(即前端)。在最近对我的 web api 进行更改之后,当我尝试反序列化前端中的 JSON 响应时,我现在总是得到一个 StackOverflowException。具体线路为:

result_ = JsonConvert.DeserializeObject<ObservableCollection<Employee>>(Encoding.UTF8.GetString(responseData_, 0, responseData_.Length));

我在这里调试的时候,看到程序在两行之间跳转,直到发生溢出:

EmployeesManager.cs(在中间件中)

private Image _image = new Image();

ImagesManager.cs(在中间件中)

private Employee _employee = new Employee();

这里有更多代码:

模型(在 Web API 中):

public class Employee
{
    public int Id { get; set; }

    // OMITTED PROPS

    public int? ImageId { get; set; }
    public Image Image { get; set; }

    public ICollection<Device> Devices { get; set; }

    public int? TeamId { get; set; }
    public Team Team { get; set; }
}

public class Image
{
    [Key]
    public int Id { get; set; }

    [Required]
    public byte[] Data { get; set; }

    public int EmployeeId { get; set; }

    public Employee Employee { get; set; }
}

客户端实现中的模型(中间件)。它们是用Nswag 生成的:

public partial class Employee : INotifyPropertyChanged
{
    private int _id;

    private int? _imageId;
    private Image _image = new Image(); // THIS LINE IS PART OF THE OVERFLOW
    private ObservableCollection<Device> _devices;
    private int? _teamId;
    private Team _team = new Team();

    // OMITTED PROPS

    [JsonProperty("image", Required = Required.Default, NullValueHandling = NullValueHandling.Ignore)]
    public Image Image
    {
        get { return _image; }
        set
        {
            if (_image != value)
            {
                _image = value;
                RaisePropertyChanged();
            }
        }
    }

public partial class Image : INotifyPropertyChanged
{
    private int _id;
    private int _employeeId;
    private Employee _employee = new Employee(); // THIS LINE IS PART OF THE STACK OVERFLOW
    private byte[] _data;

    // OMITTED PROPS 

    [JsonProperty("employee", Required = Required.Default, NullValueHandling = NullValueHandling.Ignore)]
    public Employee Employee
    {
        get { return _employee; }
        set
        {
            if (_employee != value)
            {
                _employee = value;
                RaisePropertyChanged();
            }
        }
    }

我通过 Xamarin.Forms 项目使用 Web API 客户端实现。所有平台上的行为都是相同的。不过,只有 iOS 和 UWP 能够识别 Stack Overflow。在 Android 上,当我从 Web API 读取数据时,应用程序会直接关闭而不会出现异常。

如果有人想查看更多代码,我可以准备一个包含请求代码的小包。将它们全部发布在这里会完全破坏可读性。

我使用 Newtonsoft Json.NET、Entity Framework 6、NSwag 6、Xamarin.Forms v2.3.2.127。

【问题讨论】:

  • 对象相互引用,导致无限递归。您可能需要使用 [JsonIgnore] 或类似的东西来装饰一个或多个导航属性,这样序列化程序就不会尝试遵循这些属性。
  • @zuckerthoben,您是如何生成模板的? T4?我相信你可以帮助我,请看this
  • @Shimmy 我看到你的问题已经解决了。无论如何也帮不上忙,因为我没有自动集成。我不需要它,因为我的 Web API 变化不大。

标签: c# asp.net json asp.net-web-api


【解决方案1】:

我以前也遇到过这种情况;这是由于对象之间的循环引用。您有一个 Employee 引用 Image 和 Image 引用 Employee。

尝试将[JsonIgnore] 放在图像类 Image 中的 Employee 属性上方。

【讨论】:

  • 我同意,我认为这是正确的解决方案。
  • 循环引用在 web api 中不是问题。如果存在循环引用,web api 将不会返回有效的 json。我在我的网络服务器上运行了wireshark,验证请求和响应是否有效,它们是有效的。我猜客户端实现有循环引用的问题,因此这个问题。所以我会尽量把JsonIgnore放在api客户端的image类中的navigation属性上。
  • @zuckerthoben 从 Web API 的角度来看,循环引用可能不是问题,但对于许多序列化库来说绝对是个问题。
  • @Oxidda 该死的,非常好的解决方案!非常感谢!
【解决方案2】:

我遵循了 Oxidda、David 和 EJoshuaS 的回答。

这里是完整文档的完整解决方案:

我尝试将 JsonIgnore 放在中间件(Web API 客户端 PCL)内 Image 类的 Employee 属性上。奇怪的是,这并没有解决问题。我仍然得到了堆栈溢出,其中包含属性后面的私有变量。 现在,我将 JsonIgnore 放在 Web API(后端)中 Image 类的 Employee 导航属性上以及 Device 类的 Employee 导航属性上。然后我从 API 客户端(中间件)中完全删除了导航属性(图像类中的员工和设备类中的员工),因为这些属性的 JSON 现在永远不会被接收,因为 API 已经忽略了这些。 错误现在消失了,最重要的是,我的请求和响应速度得到了显着提升。似乎尽管 Web API(后端)工作正常并且关系没有问题,但可选模型上的那些导航属性引入了大量开销。这些类真的很小,数据库的表几乎是空的,但影响似乎很大。

TL;DR:消除了源头循环引用的可能性。对客户端的镜像更改。问题解决了,速度也得到了巨大的提升。

如果有人对我的解决方案的完整设计感兴趣,这里有一个小总结。我很喜欢。

  1. 使用 Entity Framework 6 创建了一个 ASP.NET Web API (.NET 4.6.2) 项目。添加了具有关系的模型,添加了 DbContext。脚手架异步控制器。 Web API 完成。请注意,在使用带有 JSon 的实体框架时,不应使用延迟加载。相反,我在控制器中使用急切加载。
  2. 创建了一个与 Xamarin PCL 使用的配置文件相同的 PCL。因此,它与 Xamarin 解决方案兼容,但也与所有其他标准 .NET 解决方案兼容。
  3. 基于 Web API 使用NSwag 生成中间件 API。您基本上将 API.dll 加载到程序中,选择您的设置,然后您将收到一个完整的 PCL 兼容的 C# Web API 客户端实现。该 API 非常高级且异步,因此您可以在任何 .NET 前端轻松使用您的 API。
  4. 添加您选择的前端解决方案。通过客户端 API 轻松使用您的数据。

基本上,您可以在 Web API 中编写一些属性(+ 配置 JSon Serializer 和 DbContext),然后生成整个后端和中间件的其余部分。我喜欢它。

【讨论】:

    猜你喜欢
    • 2011-04-27
    • 1970-01-01
    • 2016-12-17
    • 1970-01-01
    • 1970-01-01
    • 2020-03-27
    • 2013-07-11
    • 2018-01-16
    • 2014-07-01
    相关资源
    最近更新 更多