У меня есть клиент 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
, но подходы, которые яиспользование этой опции предполагает ручную регистрацию моделей в конвертере, чего я бы хотел избежать.
У меня есть контроль над сервером и клиентским кодом, поэтому я могу реализовать любое потенциальное изменение с любой стороны.
Любая помощь будет принята с благодарностью.