2016-01-20 8 views
9

Używam Newtonsoft za Json.NET 7.0.0.0 do serializacji klas do JSON z C#:Jak pominąć pustych zbiorów podczas szeregowania z Json.NET

class Foo 
{ 
    public string X; 
    public List<string> Y = new List<string>(); 
} 

var json = 
    JsonConvert.SerializeObject(
     new Foo(), 
     Formatting.Indented, 
     new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); 

Wartość json tutaj jest

{ "Y": [] } 

, ale chciałbym, aby było { }, jeśli Y jest pustą listą.

Nie mogłem znaleźć zadowalającego sposobu, aby to osiągnąć. Może z niestandardowym rozwiązaniem rozstrzygającym umowę?

+0

Ponadto wolałbym nie dodawać atrybutów do kolekcji, ponieważ moje zajęcia mają wiele z nich i wszystkie powinny być traktowane jednakowo. –

+0

I nie możesz użyć prostego C# "if"? –

+0

@st_stefanov Jak by to działało, gdyby 'Foo' miał wiele kolekcji, tylko niektóre z nich były puste, czy chcesz wyjaśnić? :) –

Odpowiedz

9

Jeśli szukasz rozwiązania, które może być używane generycznie w różnych typach i nie wymaga żadnych modyfikacji (atrybuty itp.), To najlepsze rozwiązanie, które mogłoby mi się wydawać, gdyby było niestandardową klasą DefaultContractResolver. Używałby refleksji do określenia, czy którekolwiek z IEnumerable s dla danego typu są puste.

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

    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; 
        default: 
         break; 

       } 

       if (enumerable != null) 
       { 
        // check to see if there is at least one item in the Enumerable 
        return enumerable.GetEnumerator().MoveNext(); 
       } 
       else 
       { 
        // if the list is null, we defer the decision to NullValueHandling 
        return true; 
       } 

      }; 
     } 

     return property; 
    } 
} 
+0

Dzięki, że wygląda dokładnie to, czego potrzebuję, niestety to nie działa. Wydaje się, że 'property.DeclaringType is IEnumerable' ma zawsze wartość false. –

+0

Oto skrzypce pokazujące twój kod w akcji: https://dotnetfiddle.net/BLze2d –

+0

Masz rację - jest błąd w porównaniu. Zawsze sprawdza typ deklaracji, a nie typ własności. Zostawiłem trochę kodu, co skutkowało fałszywym pozytywem na moim końcu. Pracuję nad naprawieniem tego teraz. –

0

Jeśli można zmodyfikować swoje klasy, można dodać metodę Shrink i ustawić null dla wszystkich pustych zbiorów. Wymaga to zmiany klasy, ale ma lepszą wydajność. Po prostu kolejna opcja dla ciebie.