2016-09-08 3 views
2

При десериализации списка с TypeNameHandling.All, если пространство имен типов для одного из элементов отсутствует (удалено после сериализации), это вызовет ошибку Error resolving type specified in JSON.
Я хочу проигнорировать эти предметы, оставив остальные позади.Игнорировать недостающие типы во время десериализации списка

Error = (sender, args) => { args.ErrorContext.Handled = true; } в JsonSerializerSettings делает то, что я ищу, но, конечно, захватит ВСЕ ошибки.

Есть ли более чистый способ сделать это, возможно, через установку сериализатора, которую я пропустил?

ответ

1

Вы можете использовать следующие свойства 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.

Смежные вопросы