Исключение StackOverflowException для JsonConvert.DeserializeObject

#c# #asp.net #json #asp.net-web-api

#c# #asp.net #json #asp.net-web-api

Вопрос:

Я пишу решение, которое содержит ASP.NET Web API (.NET 4.6.2), он же серверная часть, клиентская реализация веб-API PCL, также известная как промежуточное программное обеспечение и Xamarin.Формирует проекты, также известные как интерфейс. После недавних изменений в моем веб-api я теперь всегда получаю исключение StackOverflowException при попытке десериализации ответа JSON в моем интерфейсе. Конкретная строка:

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

Вот еще код:

Модели (в веб-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();
            }
        }
    }
  

Я использую клиентскую реализацию веб-API через Xamarin.Формирует проекты. Поведение одинаково на всех платформах. Однако только iOS и UWP распознают переполнение стека. На Android приложение просто закрывается без исключения, когда я считываю данные из веб-API.

Если кто-то хочет увидеть больше кода, я могу подготовить небольшой пакет, содержащий запрошенный код. Публикация их всех здесь полностью нарушит удобочитаемость.

Я использую Newtonsoft Json.NET , Entity Framework 6, NSwag 6, Xamarin.Формы v2.3.2.127.

Комментарии:

1. Объекты взаимно ссылаются друг на друга, вызывая бесконечную рекурсию. Возможно, вам потребуется украсить одно или несколько свойств навигации [JsonIgnore] или что-то в этом роде, чтобы сериализатор не пытался следовать этим свойствам.

2. @zuckerthoben, как ты сгенерировал свой шаблон? T4? Я уверен, что вы можете мне помочь, пожалуйста, взгляните на это .

3. @Shimmy я увидел, что ваша проблема решена. В любом случае, это не помогло, потому что у меня нет автоматической интеграции. Мне это не нужно, потому что мой веб-API меняется не так сильно.

Ответ №1:

Со мной это случалось раньше; это было связано с циклической ссылкой между объектами. У вас есть изображение, ссылающееся на сотрудника, и изображение, ссылающееся на сотрудника.

Попробуйте поместить [JsonIgnore] над свойством Employee в Image класс image.

Комментарии:

1. Я согласен, я думаю, что это правильное решение.

2. Циклическая ссылка не является проблемой в веб-api. Веб-api не вернул бы действительный json, если бы была циклическая ссылка. На моем веб-сервере запущен wireshark, который проверяет, что запросы и ответы действительны, какими они и являются. Я предполагаю, что в реализации клиента есть проблемы с циклической ссылкой, отсюда и эта проблема. Поэтому я попытаюсь поместить JsonIgnore в свойство навигации в классе image клиента api.

3. @zuckerthoben С точки зрения веб-API циклическая ссылка может и не быть проблемой, но это определенно проблема для многих библиотек сериализации.

4. @Oxidda черт возьми, очень хорошее решение! Большое вам спасибо!

Ответ №2:

Я следил за ответами Оксидды, Дэвида и Эджошуа.

Вот полное решение для целей полной документации:

Я попытался поместить JsonIgnore в свойство Employee в классе Image внутри промежуточного программного обеспечения (клиент веб-API PCL). Как ни странно, это не решило проблему. Я все еще получаю переполнение стека с частными переменными, стоящими за свойствами. Теперь я помещаю JsonIgnore в свойство навигации Employee класса Image в веб-API (серверной части), а также в свойство навигации Employee класса Device. Затем я полностью удалил свойства навигации (Employee в классе image и Employee в классе device) из клиента API (промежуточное программное обеспечение), потому что JSON для этих свойств теперь никогда не будет получен, поскольку API уже будет их игнорировать. Теперь ошибка устранена, и, кроме того, я получил значительное увеличение скорости запросов и ответов. Похоже, что, хотя веб-API (серверная часть) работал нормально и не имел проблем с отношениями, эти свойства навигации в необязательных моделях приводили к большим накладным расходам. Классы действительно маленькие, а таблицы базы данных почти пустые, но влияние кажется огромным.

TL; DR: устранена возможность циклической ссылки на источник. Зеркальные изменения для клиента. Проблема решена, а также получил огромный прирост скорости.

Если кого-то интересует полный дизайн моего решения, вот небольшое резюме. Мне это очень нравится.

  1. Создал ASP.NET Проект Web API (.NET 4.6.2) с Entity Framework 6. Добавлены модели с отношениями, добавлен DbContext. Асинхронные контроллеры с каркасами. Веб-API завершен. Обратите внимание, что вы не должны использовать отложенную загрузку при использовании entity framework с JSon. Вместо этого я использую нетерпеливую загрузку в контроллерах.
  2. Создан список адресов PCL с тем же профилем, который используют списки Xamarin. Таким образом, он совместим с решениями Xamarin, а также со всеми другими стандартными решениями .NET.
  3. Сгенерировал API промежуточного программного обеспечения с помощью NSwag на основе веб-API. Вы в основном загружаете свой API.dll в программе выберите свои настройки, и вы получите полную PCL-совместимую клиентскую реализацию вашего веб-API на C #. API довольно высокоуровневый и асинхронный, поэтому вы можете легко использовать свой API в любом интерфейсе .NET.
  4. Добавьте интерфейсные решения по вашему выбору. Легко использовать ваши данные через клиентский API.

В принципе, вы можете просто написать некоторые свойства в веб-API ( настроить сериализатор JSon и DbContext), а остальное все серверное и промежуточное программное обеспечение генерируется. Мне это нравится.