Newtonsoft Json.NET可以跳过序列化空列表吗?

时间:2012-07-04 00:26:18

标签: c# list serialization json.net

我正在尝试序列化一些“懒惰创建”各种列表的遗留对象。我不能改变遗留行为。

我把它归结为这个简单的例子:

public class Junk
{
    protected int _id;

    [JsonProperty( PropertyName = "Identity" )]
    public int ID 
    { 
        get
        {
            return _id;
        }

        set
        {
            _id = value;
        }
    }

    protected List<int> _numbers;
    public List<int> Numbers
    {
        get
        {
            if( null == _numbers )
            {
                _numbers = new List<int>( );
            }

            return _numbers;
        }

        set
        {
            _numbers = value;
        }
    }
}

class Program
{
    static void Main( string[] args )
    {
        Junk j = new Junk( ) { ID = 123 };

        string newtonSoftJson = JsonConvert.SerializeObject( j, Newtonsoft.Json.Formatting.Indented );

        Console.WriteLine( newtonSoftJson );

    }
}

目前的结果是: {   “身份”:123,   “数字”:[] }

我想得到: {   “身份”:123 }

也就是说,我想跳过任何列表,集合,数组或其他空的东西。

4 个答案:

答案 0 :(得分:66)

如果您没有找到解决方案,the answer在设法追踪时非常简单。

如果允许扩展原始类,则向其添加ShouldSerializePropertyName函数。这应该返回一个布尔值,指示是否应该为该类的当前实例序列化该属性。在您的示例中,这可能看起来像这样(未经测试,但您应该得到图片):

public bool ShouldSerializeNumbers()
{
    return _numbers.Count > 0;
}

这种方法适用于我(尽管在VB.NET中)。如果您不允许修改原始类,那么链接页面上描述的IContractResolver方法就是您的选择。

答案 1 :(得分:3)

关于David Jones建议使用IContractResolver的建议,这对我来说涵盖了所有IEnumerables的变体,而无需显式修改需要序列化的类:

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

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);

        if (property.PropertyType.GetInterface(nameof(IEnumerable)) != null)
            property.ShouldSerialize =
                instance => (instance?.GetType().GetProperty(property.PropertyName).GetValue(instance) as IEnumerable<object>)?.Count() > 0;

        return property;
    }
}

然后将其构建到我的设置对象中:

static JsonSerializerSettings JsonSettings = new JsonSerializerSettings
{
    Formatting = Formatting.Indented,
    NullValueHandling = NullValueHandling.Ignore,
    DefaultValueHandling = DefaultValueHandling.Ignore,
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
    ContractResolver = ShouldSerializeContractResolver.Instance,
};

并像这样使用它:

JsonConvert.SerializeObject(someObject, JsonSettings);

答案 2 :(得分:2)

为了成为普通人,我将if测试结构化为:

public bool ShouldSerializecommunicationmethods()
{
    if (communicationmethods != null && communicationmethods.communicationmethod != null && communicationmethods.communicationmethod.Count > 0)
        return true;
    else
        return false;
}

由于空列表通常也为空。感谢您发布解决方案。 ATB。

答案 3 :(得分:2)

Bryan,您最需要的方式是不需要实例变量的开销,并且您需要捕获字段和成员实例,而且我不会运行count操作,这需要枚举来耗尽整个集合只需运行MoveNext()函数。

public class IgnoreEmptyEnumerableResolver : CamelCasePropertyNamesContractResolver
{
    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;
                }

                return enumerable == null ||
                       enumerable.GetEnumerator().MoveNext();
                // if the list is null, we defer the decision to NullValueHandling
            };
        }

        return property;
    }
}