如何在使用Json.NET进行序列化时省略空集合。

15

我正在使用Newtonsoft的Json.NET 7.0.0.0将来自C#的类序列化为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是空列表,我希望它是{ }。我找不到一个令人满意的方法来实现这一点。也许可以使用自定义合同解析器?


另外,我不太想在集合上添加属性,因为我的类有很多属性,而且它们都应该被同等对待。 - François Beaune
你不能使用简单的C#“if”吗? - st_stefanov
好的,您想在任何情况下序列化类,但要处理类内部的空集合... - st_stefanov
你也不喜欢这种方法吗?http://www.newtonsoft.com/json/help/html/ConditionalProperties.htm - st_stefanov
@st_stefanov 我看不出这种方法如何适用于我的情况。它定义了类的一个特定属性应该如何序列化。 - François Beaune
显示剩余5条评论
2个回答

14

如果你正在寻找一种可在不同类型中通用且不需要任何修改(属性等)的解决方案,那么我能想到的最佳解决方案就是使用自定义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;
    }
}

1
property.DeclaringType is IEnumerable 替换为 typeof(IEnumerable).IsAssignableFrom(property.PropertyType) && property.PropertyType != typeof(string),并将 .GetProperty(property.PropertyName).GetValue(instance, null) 替换为 .GetField(property.PropertyName).GetValue(instance),然后它就可以工作了。 - François Beaune
是的!但是您是否也希望它能够处理类属性以及字段?我已经更新了它来处理两者。 - Will Ray
最后一件事:如果合同解析器继承自CamelCasePropertyNamesContractResolver,则property.PropertyName的大小写不正确。要解决问题,请改用member.Name。 - François Beaune
@Simon 加油! - Will Ray
1
我认为可以通过以下更简单的方式获取enumerableproperty.ValueProvider?.GetValue(instance) as IEnumerable; JsonProperty已经封装了一个IValueProvider,因此我们不必使用反射的较低级别。 - Hopeless
显示剩余4条评论

0
如果您可以修改您的类,您可以添加Shrink方法并将所有空集合设置为null。这需要更改类,但它具有更好的性能。这只是另一个选项供您选择。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接