web-dev-qa-db-fra.com

Newtonsoft Json.NET peut-il ignorer la sérialisation des listes vides?

J'essaie de sérialiser des objets hérités qui "paresseux" créent différentes listes. Je ne peux pas changer le comportement de l'héritage.

Je l'ai résumé à cet exemple simple:

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 );

    }
}

Les résultats actuels sont: { "Identité": 123, "Nombres": []}

Je voudrais obtenir:{ "Identité": 123 }

C'est-à-dire que je voudrais ignorer les listes, les collections, les tableaux ou autres éléments vides.

45
Phill Campbell

Au cas où vous ne trouviez pas de solution, la réponse est remarquablement simple lorsque vous parvenez à la localiser.

Si vous êtes autorisé à étendre la classe d'origine, ajoutez-y une fonction ShouldSerializePropertyName. Cela devrait renvoyer un booléen indiquant si cette propriété doit être sérialisée ou non pour l'instance actuelle de la classe. Dans votre exemple, cela pourrait ressembler à ceci (non testé mais vous devriez obtenir l'image):

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

Cette approche fonctionne pour moi (bien que dans VB.NET). Si vous n'êtes pas autorisé à modifier la classe d'origine, l'approche IContractResolver décrite sur la page liée est la voie à suivre. 

Juste pour être commune, j'ai structuré le test si comme suit:

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

En tant que liste vide sera souvent nul aussi. Merci d'avoir posté la solution. ATB.

2
Eryn

En ce qui concerne la suggestion de David Jones d’utiliser IContractResolver, cela me permet de couvrir toutes les variations IEnumerables sans modifier explicitement la classe devant être sérialisée:

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.GetInterface(nameof(IEnumerable)) != null)
            property.ShouldSerialize =
                instance => (instance?.GetType().GetProperty(property.PropertyName).GetValue(instance) as IEnumerable<object>)?.Count() > 0;

        return property;
    }
}

Ensuite, je le construis dans mon objet settings:

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

et l'utiliser comme ça:

JsonConvert.SerializeObject(someObject, JsonSettings);
2
J Bryan Price

Bryan, vous êtes quasiment comme ici vous n’avez pas besoin de la surcharge de la variable d’instance et vous devez piéger les instances de champ et de membre. De plus, je n’exécuterais pas l’opération count qui demande à l’énumérable d’épuiser la totalité de la collection. Fonction 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;
    }
}
0
Buvy