¿Puede Newtonsoft Json.NET omitir la serialización de listas vacías?

Estoy tratando de serializar algunos objetos heredados que "lazy crea" varias listas. No puedo cambiar el comportamiento heredado.

Lo he reducido a este simple ejemplo:

public class Junk
{
    protected int _id;

    [JsonProperty( PropertyName = "Identity" )]
    public int ID 
    { 
        get
        {
            return _id;
        }

        set
        {
            _id = value;
        }
    }

    protected List<int> _numbers;
    public List<int> Numbers
    {
        get
        {
            if( null == _numbers )
            {
                _numbers = new List<int>( );
            }

            return _numbers;
        }

        set
        {
            _numbers = value;
        }
    }
}

class Program
{
    static void Main( string[] args )
    {
        Junk j = new Junk( ) { ID = 123 };

        string newtonSoftJson = JsonConvert.SerializeObject( j, Newtonsoft.Json.Formatting.Indented );

        Console.WriteLine( newtonSoftJson );

    }
}

Los resultados actuales son: { "Identidad": 123, "Números": [] }

Me gustaría obtener: { "Identidad": 123 }

Es decir, me gustaría omitir cualquier lista, colección, matriz o cosas que estén vacías.

preguntado el 04 de julio de 12 a las 01:07

4 Respuestas

En caso de que no haya encontrado una solución a esto, la respuesta es notablemente simple cuando logras rastrearlo.

Si se le permite ampliar la clase original, agregue un ShouldSerializePropertyName función a ello. Esto debería devolver un valor booleano que indique si esa propiedad debe serializarse o no para la instancia actual de la clase. En su ejemplo, esto podría verse así (no probado, pero debería obtener una imagen):

public bool ShouldSerializeNumbers()
{
    return _numbers.Count > 0;
}

Este enfoque funciona para mí (aunque en VB.NET). Si no se le permite modificar la clase original, entonces el IContractResolver El enfoque descrito en la página vinculada es el camino a seguir.

Respondido 07 Oct 15, 14:10

puedo hacer de forma genérica? No conozco todos los nombres de propiedades, pero quiero que todas las matrices vacías sean nulas. - Rohit

¡Esto también funciona para listas genéricas! Lista pública archivos adjuntos { obtener; colocar; } /// /// Evite archivos adjuntos JSON vacíos; de lo contrario, SendGrid rechazará /// public bool DeberíaSerializar archivos adjuntos () { // newtonsoft.com/json/help/html/ConditionalProperties.htm // No serialice la matriz de archivos adjuntos si no tiene ningún elemento que devuelva archivos adjuntos. Recuento > 0; } - Robnick

Con respecto a la sugerencia de David Jones de usar IContractResolver, esto funciona para mí para cubrir todo IEnumerables variaciones sin modificar explícitamente la clase que necesita ser serializada:

public class ShouldSerializeContractResolver : DefaultContractResolver
{
    public static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();

        protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
            JsonProperty property = base.CreateProperty(member, memberSerialization);

            if (property.PropertyType != typeof(string)) {
                if (property.PropertyType.GetInterface(nameof(IEnumerable)) != null)
                    property.ShouldSerialize =
                        instance => (instance?.GetType().GetProperty(property.PropertyName).GetValue(instance) as IEnumerable<object>)?.Count() > 0;
            }
            return property;
        }
}

Luego lo construyo en mi objeto de configuración:

static JsonSerializerSettings JsonSettings = new JsonSerializerSettings
{
    Formatting = Formatting.Indented,
    NullValueHandling = NullValueHandling.Ignore,
    DefaultValueHandling = DefaultValueHandling.Ignore,
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
    ContractResolver = ShouldSerializeContractResolver.Instance,
};

y utilícelo así:

JsonConvert.SerializeObject(someObject, JsonSettings);

Respondido el 02 de diciembre de 19 a las 23:12

Gracias por la respuesta. Descubrí que tenía que reemplazar los dos ifs con uno solo para que funcione sin embargo: if (property.PropertyType.GetInterface(nameof(IEnumerable)) != null) - tom Robinson

@TomRobinson - ¡Gracias, es útil saberlo! Mi memoria de las tuercas y tornillos de .NET está oxidada jeje - Precio de J Bryan

Tuve que cambiar la linea instance => (instance?.GetType().GetProperty(property.PropertyName).GetValue(instance) as IEnumerable<object>)?.Count() > 0; a instance => (instance?.GetType().GetProperty(property.PropertyName)?.GetValue(instance) as IEnumerable)?.OfType<object>().Count() > 0; de lo contrario, los objetos en IEnumerables no vacíos se serializarían como "{}" - henk kok

Creo que también deberíamos return base.CreateProperty(member, memberSerialization); en lugar de solo return property en la última línea. Y return property sólo si encontramos un IEnumerable. - tom Robinson

Deberías usar GetProperty(property.UnderlyingName) en lugar de GetProperty(property.PropertyName), en caso de que esté utilizando una estrategia de nomenclatura. - Mika Tahtinen

Bryan, estás casi en el camino allí, no necesitas la sobrecarga de la variable de instancia y necesitas atrapar las instancias de campo y miembro, además, no ejecutaría la operación de conteo que requiere el enumerable para agotar toda la colección, simplemente puedes ejecutar el Función MoveNext().

public class IgnoreEmptyEnumerableResolver : CamelCasePropertyNamesContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member,
        MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);

        if (property.PropertyType != typeof(string) &&
            typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
        {
            property.ShouldSerialize = instance =>
            {
                IEnumerable enumerable = null;
                // this value could be in a public field or public property
                switch (member.MemberType)
                {
                    case MemberTypes.Property:
                        enumerable = instance
                            .GetType()
                            .GetProperty(member.Name)
                            ?.GetValue(instance, null) as IEnumerable;
                        break;
                    case MemberTypes.Field:
                        enumerable = instance
                            .GetType()
                            .GetField(member.Name)
                            .GetValue(instance) as IEnumerable;
                        break;
                }

                return enumerable == null ||
                       enumerable.GetEnumerator().MoveNext();
                // if the list is null, we defer the decision to NullValueHandling
            };
        }

        return property;
    }
}

Respondido el 04 de enero de 19 a las 06:01

Todos mis esquemas de datos no tenían campos definidos, por lo que no me encontré con este caso. ¡Gracias! ¿Necesita el condicional nulo en MemberTypes.Property pero no en MemberTypes.Field por alguna razon en especifico? Además, no pensé que cadena los objetos se incluirían en mi cheque, pero la lógica aún debería aplicarse, ¿verdad? ¿O es mejor diferir el manejo de ellos a Json.Net? - Precio de J Bryan

Solo para ser colgante de jardín común, estructuré la prueba if para que sea:

public bool ShouldSerializecommunicationmethods()
{
    if (communicationmethods != null && communicationmethods.communicationmethod != null && communicationmethods.communicationmethod.Count > 0)
        return true;
    else
        return false;
}

Como una lista vacía a menudo también será nula. Gracias, por publicar la solución. ATB.

Respondido 20 ago 15, 21:08

Otra opción que tiene es hacer uso del atributo "NullValueHandling": [JsonProperty("yourPropertyName", NullValueHandling = NullValueHandling.Ignore)]. Eso debería ayudar a reducir la necesidad de verificaciones nulas, lo que refinará un poco sus verificaciones if. Solo pensé en mencionarlo, ya que puede resultarle útil. - Erick Brown

En el método ShouldSerialize, aún necesita verificar los valores nulos; de lo contrario, una llamada a ShouldSerialize generará una excepción en los valores nulos. Incluso si el serializador está codificado para ignorar las excepciones al verificar los métodos ShouldSerialize, la excepción que se genere agregará una sobrecarga innecesaria que podría afectar el rendimiento. Por lo tanto, es posible que gane mucho más de lo que pierde al verificar primero el valor nulo. - Chris Schaller

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.