François Beaune François Beaune - 5 months ago 57
JSON Question

How to omit empty collections when serializing with Json.NET

I'm using Newtonsoft's Json.NET 7.0.0.0 to serialize classes to JSON from C#:

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


The value of
json
here is

{ "Y": [] }


but I would like it to be
{ }
if
Y
is an empty list.

I couldn't find a satisfactory way to achieve this. Maybe with a custom contract resolver?

Answer

If you're looking for a solution which can be used generically across different types and does not require any modification (attributes, etc), then the best solution that I can think if would be a custom DefaultContractResolver class. It would use reflection to determine if any IEnumerables for a given type are empty.

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