Ignorando los miembros de la clase que lanzar excepciones cuando serialización a JSON

Estoy usando el Newtonsoft JSON serializador y funciona para la mayoría de los objetos.

Por desgracia tengo un JsonSerializationException cuando trato de serializar un objeto de gran tamaño, uno de cuyos miembros lanza una NullReferenceException.

Es de todos modos hay que ignorar el miembro infractor y serializar el resto de los objetos?

Estoy pensando tal vez en la JsonSerializerSettings?

Aquí una versión simplificada de lo que quiero hacer:

private class TestExceptionThrowingClass
{
    public string Name { get { return "The Name"; } }
    public string Address { get { throw new NullReferenceException(); } }
    public int Age { get { return 30; } }
}

[Test]
public void CanSerializeAClassWithAnExceptionThrowingMember()
{ 
    //Arrange
    var toSerialize = new TestExceptionThrowingClass();

    //Act

    var serializerSettings = new Newtonsoft.Json.JsonSerializerSettings();
    serializerSettings.MaxDepth = 5;
    serializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
    serializerSettings.MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Ignore;
    serializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
    serializerSettings.ObjectCreationHandling = Newtonsoft.Json.ObjectCreationHandling.Reuse;
    serializerSettings.DefaultValueHandling = Newtonsoft.Json.DefaultValueHandling.Ignore;

    var result = Newtonsoft.Json.JsonConvert.SerializeObject(toSerialize);

    //Assert
    Assert.That(result, Is.EqualTo(@"{""Name"":""The Name"",""Age"":30}"));
}

Y aquí está el seguimiento de la pila:

at Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target) 
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues(JsonWriter writer, Object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, JsonContract& memberContract, Object& memberValue) 
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) 
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty) 
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType) 
at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType) 
at Newtonsoft.Json.JsonConvert.SerializeObject(Object value, Type type, Formatting formatting, JsonSerializerSettings settings) 
at Newtonsoft.Json.JsonConvert.SerializeObject(Object value) 
at AspectsProject.Aspects.CachingPolicy.CachingPolicyCacheKeyCreatorTests.CanSerializeAClassWithAnExceptionThrowingMember() in D:\Dev\test.cs:line 169
    --NullReferenceException 
at AspectsProject.Aspects.CachingPolicy.CachingPolicyCacheKeyCreatorTests.TestExceptionThrowingClass.get_Address() in D:\Dev\test.cs:line 149 
at GetAddress(Object ) 
at Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target)

Estoy feliz de usar diferentes JSON serializador si alguien sabe que hacer esto.

  • Sólo para aclarar, es el miembro de la realidad null o lo hace de forma explícita lanzar una NullReferenceException cuando se accede a él?
  • El miembro lanza una excepción NullReferenceException con acceso.
  • Es correcto asumir que no tenemos control sobre el código fuente para el miembro infractor?
  • Extraño – esta pregunta describe un problema similar y la configuración de NullValueHandling a NullValueHandling.Ignore parece ser la solución. Pregunto, ¿qué es diferente en su caso….
  • Puede pegar la excepción info (mensaje, el seguimiento de la pila) aquí?
  • Yup. Eso es cierto.
  • mi caso es un poco diferente, ya que el valor de la propiedad es null en que uno. En el mío, el de la propiedad, se produce una excepción.
  • Es información confidencial, pero voy a añadir algo más de información a la pregunta. Sólo necesitas un minuto para escribir…. no tomará mucho tiempo.
  • y es hacia arriba. Déjame saber lo que piensas. Para volver a repetir, estoy feliz de usar diferentes serializador si uno diferente de las obras.
  • Si tiene varias instancias de una Clase, algunas de las cuales (para algunos desconocida arcana razón) lanza una excepción cuando tiene acceso a una propiedad, entonces esto suena como usted tiene mayores problemas con los que lidiar antes de preocuparse acerca de la conversión a JSON. Si se puede predecir cuando se producirá una excepción se podría construir un contenedor para proteger contra la excepción y tiene que devolver null lugar por lo que el defecto NullValueHandling regla funciona.
  • Yo estaba buscando una solución de otro problema: por qué me JSonSerilizationError. Lo resolvió para mí: «uno de los que los miembros de la lanza una excepción NullReferenceException.» Gracias

InformationsquelleAutor Ev. | 2014-01-07

2 Kommentare

  1. 28

    Si no controlas el código fuente, puede utilizar una costumbre ContractResolver para inyectar un «ShouldSerialize» método para la problemática de la propiedad durante la serialización. Usted puede tener ese método siempre devuelve false, u opcionalmente, aplicar un poco de lógica, que detecta las situaciones en las que la propiedad va a lanzar y devolver false sólo en ese caso.

    Por ejemplo, digamos que tu clase se parece a esto:

    class Problematic
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public object Offender 
        {
            get { throw new NullReferenceException(); }
        }
    }

    Claramente, si tratamos de serializar el de arriba, no va a funcionar porque el Offender propiedad siempre lanzar una excepción cuando el serializador intenta acceder a él. Ya sabemos la clase y el nombre de la propiedad que causa el problema, podemos escribir una costumbre ContractResolver (derivado de la DefaultContractResolver) para suprimir la serialización de que el miembro específico.

    class CustomResolver : DefaultContractResolver
    {
        protected override JsonProperty CreateProperty(MemberInfo member, 
                                            MemberSerialization memberSerialization)
        {
            JsonProperty property = base.CreateProperty(member, memberSerialization);
    
            if (property.DeclaringType == typeof(Problematic) && 
                property.PropertyName == "Offender")
            {
                property.ShouldSerialize = instanceOfProblematic => false;
            }
    
            return property;
        }
    }

    Aquí un demo que muestra cómo usarlo:

    class Program
    {
        static void Main(string[] args)
        {
            Problematic obj = new Problematic
            {
                Id = 1,
                Name = "Foo"
            };
    
            JsonSerializerSettings settings = new JsonSerializerSettings();
            settings.ContractResolver = new CustomResolver();
    
            string json = JsonConvert.SerializeObject(obj, settings);
            Console.WriteLine(json);
        }
    }

    De salida:

    {"Id":1,"Name":"Foo"}

    Una solución más genérica

    En sus comentarios que indican que usted tiene muchos tipos de objetos que podría lanzar una excepción cuando ninguna de las propiedades que se accede. Para ello, se necesita algo más genérico. Aquí está una resolución que podría funcionar para ese caso, pero tendrás que probar extensamente en su propio entorno. No depende de ninguna clase en particular o nombre de la propiedad, sino que crea un ShouldSerialize predicado por cada propiedad que viene su manera. En que el predicado se utiliza la reflexión para obtener el valor de la propiedad dentro de un try/catch; si tiene éxito, devuelve true, y false en caso contrario.

    class CustomResolver : DefaultContractResolver
    {
        protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
        {
            JsonProperty property = base.CreateProperty(member, memberSerialization);
    
            property.ShouldSerialize = instance =>
            {
                try
                {
                    PropertyInfo prop = (PropertyInfo)member;
                    if (prop.CanRead)
                    {
                        prop.GetValue(instance, null);
                        return true;
                    }
                }
                catch
                {
                }
                return false;
            };
    
            return property;
        }
    }

    Aquí es un demo:

    class Program
    {
        static void Main(string[] args)
        {
            List<MightThrow> list = new List<MightThrow>
            {
                new MightThrow { Flags = ThrowFlags.None, Name = "none throw" },
                new MightThrow { Flags = ThrowFlags.A, Name = "A throws" },
                new MightThrow { Flags = ThrowFlags.B, Name = "B throws" },
                new MightThrow { Flags = ThrowFlags.Both, Name = "both throw" },
            };
    
            JsonSerializerSettings settings = new JsonSerializerSettings();
            settings.ContractResolver = new CustomResolver();
            settings.Formatting = Formatting.Indented;
    
            string json = JsonConvert.SerializeObject(list, settings);
            Console.WriteLine(json);
        }
    }
    
    [Flags]
    enum ThrowFlags
    {
        None = 0,
        A = 1,
        B = 2,
        Both = 3
    }
    
    class MightThrow
    {
        public string Name { get; set; }
        public ThrowFlags Flags { get; set; }
    
        public string A
        {
            get
            {
                if ((Flags & ThrowFlags.A) == ThrowFlags.A)
                    throw new Exception();
                return "a";
            }
        }
    
        public string B
        {
            get
            {
                if ((Flags & ThrowFlags.B) == ThrowFlags.B)
                    throw new Exception();
                return "b";
            }
        }
    }

    De salida:

    [
      {
        "Name": "none throw",
        "Flags": 0,
        "A": "a",
        "B": "b"
      },
      {
        "Name": "A throws",
        "Flags": 1,
        "B": "b"
      },
      {
        "Name": "B throws",
        "Flags": 2,
        "A": "a"
      },
      {
        "Name": "both throw",
        "Flags": 3
      }
    ]
    • Muy cool! Me gusta. El único problema es que no podía ser de varias clases diferentes que se serializa que podría lanzar una excepción. Así que la única manera de detectar si la propiedad se va a lanzar una excepción es try…catch es. Puedo hacer esto en su solución?
    • Puede dar un ejemplo de lo que quieres decir? Estás diciendo que el Delincuente propiedad podría contener una instancia de cualquiera de varios objetos, y algunos de tiro, mientras que otros no?
    • Tengo un montón de objetos diferentes que me gustaría para serializar. Así que no podría ser una excepción en cualquiera de ellos. Eso significa que yo no puedo decir: «si el tipo de objeto es XXX y el nombre de miembro se YYY entonces no serializar». en lugar tengo que decir «intentar obtener el valor de la corriente miembro que se va a serializar. Si se produce una excepción, no serializar. Si no tirar, hacer serializar»
    • protegidas reemplazar JsonProperty CreateProperty() { JsonProperty propiedad = base.CreateProperty(miembro, memberSerialization); try { base.CreateMemberValueProvider(miembro).GetValue(propiedad); } catch(Exception ex) { propiedad.ShouldSerialize = instanceOfProblematic => false; } return propiedad; }
    • El de arriba no es bonito (y lo que no), pero eso es más o menos lo que estoy pensando.
    • Así que estás diciendo que cualquier propiedad de cualquier objeto potencialmente podría tirar, y desea una solución genérica que se hace try/catch en todo?
    • Eso sería lo ideal. Gracias por la ayuda.
    • OK, tengo algo que parece funcionar (ver respuesta actualizada). Su kilometraje puede variar.
    • Woah, eres increíble. Muchas gracias! Funciona a la perfección. Ahora mi única preocupación es que cuando la gente mira mi código pensarán que soy mucho más inteligente de lo que realmente soy. Voy a añadir un comentario en el código de la vinculación a este post así que no hay confusión. Gracias hombre!
    • LOL! Me alegro de que podría ayudar.

  2. 44

    Una manera más sencilla para ignorar los errores:

    JsonSerializerSettings settings = new JsonSerializerSettings();
    settings.Error = (serializer,err) => {
        err.ErrorContext.Handled = true;
    }

    o

    settings.Error = (serializer,err) => err.ErrorContext.Handled = true;
    • Un error de cualquier tipo? Sólo pretendo que no obtendrá un error! ¿Qué podría salir mal?
    • Yo normalmente estoy de acuerdo contigo, pero creo que este es un criterio basado en la situación. Estoy tratando de volcar los objetos en el disco para que pueda compararlas fácilmente y realmente no tengo ni ganas de gastar el siguiente par de horas tratando de conseguir un analizador personalizado de trabajo para el código que estoy utilizando únicamente para fines de solución de problemas. El objeto de la que estoy trabajando es en una biblioteca y no puedo cambiar cómo funciona. Si me llaman de una manera, un conjunto de propiedades que no son válidos y si me llaman de otro, un conjunto diferente no son válidos. Por lo que su esto o analizador personalizado.
    • Estoy de acuerdo en que este es un hack usted puede ser que necesite para usar si usted no tiene ninguna opción pero para escribir el código de malo como una solución para algunos el código de malo que no controlas.
    • Funciona como un encanto. Gracias.
    • Solución agradable. THX! @EKW: de hecho, Hay situaciones donde sólo se desea obtener tanta información como sea posible sin tener que pelearnos con excepciones. Para mí fue un extremo de API para las pruebas de que los vertederos de todo tipo de solicitud de información y la configuración del servidor para su posterior análisis. La producción de No listo de código, pero muy necesario para plan y diseño el código final.
    • Mala código es el código de malo. Hay razones para escribir el código de malo cuando es necesario, pero esto es claramente mala, y la gente va ABSOLUTAMENTE uso en los sistemas de producción.
    • He encontrado esta útil arbitrarias de registro de los objetos, donde no me importa si algo falla la seriación.

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea