Десериализовать нераспознанный тип в существующий неизвестный объект по умолчанию с json.net - PullRequest
2 голосов
/ 05 апреля 2019

У меня есть клиент API, который получает сериализованные данные JSON из ASP.NET Web API.Сериализация и десериализация происходит с JSON.Net.Сериализованный JSON содержит информацию о типах, используя TypeNameHandling = TypeNameHandling.Objects, которая работает как обычно в обычных случаях использования.

Проблема

Проблема заключается в том, что модели домена часто меняются на стороне сервера,но потребители не обязательно извлекают эти обновления немедленно.

Когда это происходит, потребитель запрашивает список моделей, но сериализация выдает ошибку, и потребитель вообще не может видеть возвращенные модели, даже еслинеизвестен только один из списка многих возвращаемых объектов.

Я знаю, что могу проглотить ошибку с помощью ErrorContext.Handled = true;, но это поглотит каждую ошибку, и я действительно не хочу этого.

ЖелаемыйПример поведения / домена

Я хочу сериализовать эти «нераспознанные» элементы как специально определенные неизвестные элементы.Я также был бы открыт для их сериализации в качестве базового класса (хотя сейчас это абстрактно), если это более реалистично.

Я хочу, чтобы решение было гибким в том случае, когда новый тип (новыйтип фруктов в примере домена ниже) добавляется к домену, его нужно добавить только в одном месте.Я не хочу вручную регистрировать фрукты в конкретном сериализованном классе.

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

Вот пример - потерпите меня на моем упрощенном примере домена:

Модели

public class FruitResponse
{
   public int ResponseId {get; set;}
   public Fruit Fruit {get; set;}
}

public abstract class Fruit
{
   public string Color {get; set;}
   public decimal Weight {get; set;}
}

public class Banana : Fruit
{
   public decimal Length {get; set;}
}

public class Apple : Fruit
{
   public int WormCount {get; set;}
}

public class UnknownFruit : Fruit
{
   //whatever
}

Сценарий

Итак, скажем клиентделает запрос на фрукты, и сервер возвращает список FruitResponse объектов.Пока фрукты внутри этих предметов - яблоки и бананы, проблем нет.Однако, если на стороне сервера добавлен новый тип фруктов "Orange", а свойство $type возвращенного JSON отражает это, запрос не будет выполнен.

Я хочу Orange (и любые потенциальныеманго, клубника, что угодно еще) чтобы быть десериализованным как Неизвестный фрукт.Таким образом, потребитель знает, что есть дополнительные фрукты, видит свойства FruitResponse этих объектов (FruitId), а также свойства базовых фруктов.В результате, новые добавления на стороне сервера не создают критических изменений для клиентов.Любые специфические свойства Orange, поступающие с сервера, можно игнорировать.

Очевидно, я хочу это поведение только для предметов, которые являются фруктами.Все они унаследованы от базового класса Fruit, а также все они будут в пространстве имен Fruit.

Я точно знаю, что все новые фрукты будут добавлены в то же пространство имен, поэтому яинтересно, есть ли способ проверить это в поле $type при десериализации.

Если возвращается Vegetable или Foo, я согласен с ошибкой сериализации.

То, что я пробовал

Я пытался зафиксировать ошибку внутри пользовательского ISerializationBinder с помощью

    public Type BindToType(string assemblyName, string typeName)
    {
        try
        {
            return binder.BindToType(assemblyName, typeName);
        }
        catch (Exception ex)
        {
            return typeof(UnknownFruit);
        }
    }

Но это не только кажется немного странным (использование исключения для потока управления) тоже не работает;Я получаю эту ошибку (игнорирую нечетное пространство имен, это от меня, играющего в linqpad):

Тип, указанный в JSON 'UserQuery + UnknownFruit, query_tykmkh, Версия = 0.0.0.0, Культура = нейтральный,PublicKeyToken = null 'не совместим с' UserQuery + Fruit, query_rraqao, версия = 0.0.0.0, Culture = нейтральный, PublicKeyToken = null '.Путь «Инструмент. $ Type», строка 1, позиция 124.

Он также не позволяет мне исследовать $type, чтобы определить, находится ли он в нужном пространстве имен фруктов.

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

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

У меня есть контроль над сервером и клиентским кодом, поэтому я могу реализовать любое потенциальное изменение с любой стороны.

Любая помощь будет принята с благодарностью.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...