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


我正在使用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 是一个空列表。

我无法找到一种令人满意的方法来实现这一目标。也许有自定义合约解析?


8508
2018-01-20 14:47


起源

另外,我宁愿不在集合中添加属性,因为我的类有很多,并且所有这些都应该被平等对待。 - François Beaune
你不能使用简单的C#“如果”? - st_stefanov
@st_stefanov如果有的话会怎么样? Foo 有多个集合,其中只有一些是空的,关心解释? :) - François Beaune
好吧,你想要在任何情况下序列化类,但处理类中的空集合... - st_stefanov
你也不喜欢这种方法吗? newtonsoft.com/json/help/html/ConditionalProperties.htm - st_stefanov


答案:


如果您正在寻找一种可以在不同类型中使用的解决方案,并且不需要任何修改(属性等),那么我认为最好的解决方案是自定义的 DefaultContractResolver 类。它会使用反射来确定是否有 IEnumerable给定类型的s是空的。

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

12
2018-01-20 15:18



谢谢,这看起来正是我需要的,不幸的是它似乎不起作用。这好像是 property.DeclaringType is IEnumerable 总是假的。 - François Beaune
这是一个显示您的代码的小提琴: dotnetfiddle.net/BLze2d - François Beaune
你是对的 - 比较中有一个错误。它总是在查看声明类型,而不是属性类型。我留下了一些代码,导致我的结果出现误报。正在努力修复它。 - Will Ray
更换 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


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


0
2018-06-24 05:50