web-dev-qa-db-de.com

Kann Newtonsoft Json.NET die Serialisierung leerer Listen überspringen?

Ich versuche, einige ältere Objekte zu serialisieren, die "faul" verschiedene Listen erstellen. Ich kann das Vermächtnis nicht ändern.

Ich habe es auf dieses einfache Beispiel verkürzt:

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

    }
}

Die aktuellen Ergebnisse sind: { "Identität": 123, "Zahlen": [] }

Ich hätte gern:{ "Identität": 123 }

Das heißt, ich möchte alle Listen, Sammlungen, Arrays oder solche leeren Objekte überspringen.

45
Phill Campbell

Falls Sie keine Lösung für dieses Problem gefunden haben, ist die Antwort bemerkenswert einfach, wenn Sie es aufspüren.

Wenn Sie die ursprüngliche Klasse erweitern dürfen, fügen Sie ihr eine ShouldSerializePropertyName-Funktion hinzu. Dies sollte einen Booleschen Wert zurückgeben, der angibt, ob diese Eigenschaft für die aktuelle Instanz der Klasse serialisiert werden soll. In Ihrem Beispiel könnte dies so aussehen (nicht getestet, aber Sie sollten das Bild erhalten):

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

Dieser Ansatz funktioniert für mich (wenn auch in VB.NET). Wenn Sie die ursprüngliche Klasse nicht ändern dürfen, ist der auf der verlinkten Seite beschriebene IContractResolver-Ansatz der richtige Weg. 

Um pendant zu sein, habe ich den if-Test folgendermaßen strukturiert:

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

Da eine leere Liste oft auch null ist. Vielen Dank für die Veröffentlichung der Lösung. ATB.

2
Eryn

Im Hinblick auf den Vorschlag von David Jones, IContractResolver zu verwenden, kann ich damit alle IEnumerables-Varianten abdecken, ohne die Klasse, die serialisiert werden muss, explizit zu ändern:

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

Dann baue ich es in mein Einstellungsobjekt ein:

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

und benutze es so:

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

Bryan, Sie sind am meisten dort, Sie brauchen nicht den Overhead der Instanzvariablen und Sie müssen sowohl die Feld- als auch die Elementinstanzen abfangen. Außerdem würde ich die Count-Operation nicht ausführen Funktion 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