Вы можете использовать следующие свойства ErrorContext
внутри SerializationErrorCallback
, чтобы ограничить типы ошибок, которые необходимо обрабатывать и игнорировать:
ErrorEventArgs.ErrorContext.OriginalObject
: получает исходный объект, вызвавший ошибку. . Если элемент списка не может быть построен из-за недопустимого имени типа, OriginalObject
будет самим списком.
С помощью этого свойства вы можете проверить, является ли OriginalObject
IList<T>
для некоторого T
.
ErrorEventArgs.CurrentObject
. Получает текущий объект, для которого вызывается событие ошибки. Исключения всплывают в стеке вызовов сериализации, и объекты на каждом уровне могут попытаться обработать ошибку.
Вы захотите обрабатывать его на самом низком уровне, когда CurrentObject == ErrorContext.OriginalObject
.
ErrorEventArgs.ErrorContext.Error
— вызывает фактическое исключение. Вам потребуется обрабатывать только исключения, создаваемые привязкой сериализации.
Теперь, как обнаружить и перехватить только те исключения из-за неудачной привязки имени типа? Как оказалось, DefaultSerializationBinder
выдает JsonSerializationException
, когда тип не может быть загружен. Однако то же самое исключение может быть вызвано во многих других ситуациях, включая неверный файл JSON. Итак, введите ISerializationBinder
декоратор, который перехватывает и перехватывает исключения из связывателя JSON по умолчанию и упаковывает их в исключения определенного типа:
public class JsonSerializationBinder : ISerializationBinder
{
readonly ISerializationBinder binder;
public JsonSerializationBinder(ISerializationBinder binder)
{
if (binder == null)
throw new ArgumentNullException();
this.binder = binder;
}
public Type BindToType(string assemblyName, string typeName)
{
try
{
return binder.BindToType(assemblyName, typeName);
}
catch (Exception ex)
{
throw new JsonSerializationBinderException(ex.Message, ex);
}
}
public void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
binder.BindToName(serializedType, out assemblyName, out typeName);
}
}
public class JsonSerializationBinderException : JsonSerializationException
{
public JsonSerializationBinderException() { }
public JsonSerializationBinderException(string message) : base(message) { }
public JsonSerializationBinderException(string message, Exception innerException) : base(message, innerException) { }
public JsonSerializationBinderException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
Далее, на каком-то более высоком уровне кода Json.NET упаковывает JsonSerializationBinderException
внутри еще одного JsonSerializationException
, поэтому необходимо просмотреть внутренние исключения на наличие исключения нужного типа при принятии решения об обработке исключения. Следующие настройки делают свое дело:
var settings = new JsonSerializerSettings
{
SerializationBinder = new JsonSerializationBinder(new DefaultSerializationBinder()),
TypeNameHandling = TypeNameHandling.All, // Or Auto or Objects as appropriate
Error = (sender, args) =>
{
if (args.CurrentObject == args.ErrorContext.OriginalObject
&& args.ErrorContext.Error.InnerExceptionsAndSelf().OfType<JsonSerializationBinderException>().Any()
&& args.ErrorContext.OriginalObject.GetType().GetInterfaces().Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IList<>)))
{
args.ErrorContext.Handled = true;
}
},
};
Используя метод расширения:
public static class ExceptionExtensions
{
public static IEnumerable<Exception> InnerExceptionsAndSelf(this Exception ex)
{
while (ex != null)
{
yield return ex;
ex = ex.InnerException;
}
}
}
Демонстрационная скрипта №1 здесь.
Обратите внимание, что ISerializationBinder
был представлен в Json.NET 10.0.1. . В более ранних версиях ваша оболочка должна наследовать от SerializationBinder
и установить в JsonSerializerSettings.Binder
.
Демонстрационная скрипта № 2 здесь.
08.09.2016