Вы можете использовать следующие свойства ErrorContext
внутри SerializationErrorCallback
ограничить типы ошибок, которые будут обработаны и игнорируемых:
ErrorEventArgs.ErrorContext.OriginalObject
: это получает исходный объект, который вызвал ошибку. Если элемент списка не может быть создан из-за недопустимого имени типа, то этот список будет OriginalObject
.
С данным видом недвижимости вы можете проверить, есть ли OriginalObject
- IList<T>
для некоторых T
.
ErrorEventArgs.CurrentObject
. Получает текущий объект, с которым связано событие ошибки. Исключения пузыряют стек вызовов сериализации, и объекты на каждом уровне могут попытаться обработать ошибку.
Вам нужно будет обработать его на самом низком уровне, когда CurrentObject == ErrorContext.OriginalObject
.
ErrorEventArgs.ErrorContext.Error
- получает фактическое исключение. Вы хотите обрабатывать только исключения, сброшенные serialization binder.
Теперь, как обнаружить и уловить только те исключения из-за неудачной привязки имени типа? Как выясняется, DefaultSerializationBinder
Json.NET выбрасывает JsonSerializationException
, когда тип не может быть загружен. Однако одно и то же исключение может быть использовано во многих других ситуациях, в том числе в некорректном файле JSON. Таким образом, ввести обертку SerializationBinder
, что ловит и перехватывает исключения из вяжущего JSON и упаковывает их в определенный тип исключения:
public class JsonSerializationBinder : SerializationBinder
{
readonly SerializationBinder binder;
public JsonSerializationBinder(SerializationBinder binder)
{
if (binder == null)
throw new ArgumentNullException();
this.binder = binder;
}
public override Type BindToType(string assemblyName, string typeName)
{
try
{
return binder.BindToType(assemblyName, typeName);
}
catch (Exception ex)
{
throw new JsonSerializationBinderException(ex.Message, ex);
}
}
public override 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
{
TypeNameHandling = TypeNameHandling.All,
Binder = new JsonSerializationBinder(new DefaultSerializationBinder()),
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;
}
}
}
Sample fiddle.