Как опустить пустые коллекции при сериализации с помощью Json.NET

Я использую Newtonsoft Json.NET 7.0.0.0 для сериализации классов для JSON из С#:

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

Значение json здесь

{ "Y": [] }

но я бы хотел, чтобы он был { }, если Y - пустой список.

Я не мог найти удовлетворительного пути для достижения этого. Может быть, с пользовательским соглашением?

Ответ 1

Если вы ищете решение, которое можно использовать в общем для разных типов и не требующее каких-либо изменений (атрибутов и т.д.), То лучшим решением, которое я могу подумать, будет пользовательский класс DefaultContractResolver. Это использовало бы отражение, чтобы определить, являются ли какие-либо IEnumerable для данного типа пустыми.

public class IgnoreEmptyEnumerablesResolver : DefaultContractResolver
{
    public 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;
    }
}

Ответ 2

Если вы можете изменить свои классы, вы можете добавить метод Shrink и установить значение null для всех пустых коллекций. Это требует изменения класса, но оно имеет лучшую производительность. Еще один вариант для вас.