【问题标题】:Can Newtonsoft Json.NET skip serializing empty lists?Newtonsoft Json.NET 可以跳过序列化空列表吗?
【发布时间】:2012-07-04 00:26:17
【问题描述】:

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

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

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 }

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

【问题讨论】:

    标签: c# list serialization json.net


    【解决方案1】:

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

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

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

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

    【讨论】:

    • 我可以用通用的方式吗?我不知道所有属性名称,但希望所有空数组都为空。
    • 这也适用于通用列表!公共列表 附件 { 获取;放; } /// /// 防止空附件 JSON - 否则 SendGrid 将拒绝 /// public bool ShouldSerializeattachments() { // newtonsoft.com/json/help/html/ConditionalProperties.htm // 如果没有序列化附件数组,则不要序列化' t 有任何项目返回附件。计数 > 0; }
    【解决方案2】:

    关于 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 != typeof(string)) {
                    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);
    

    【讨论】:

    • 感谢您的回答。我发现我必须用一个替换两个 ifs 才能让它工作:if (property.PropertyType.GetInterface(nameof(IEnumerable)) != null)
    • @TomRobinson - 谢谢,知道这很有帮助!我对 .NET 细节的记忆已经生锈了呵呵
    • 我必须将行 instance =&gt; (instance?.GetType().GetProperty(property.PropertyName).GetValue(instance) as IEnumerable&lt;object&gt;)?.Count() &gt; 0; 更改为 instance =&gt; (instance?.GetType().GetProperty(property.PropertyName)?.GetValue(instance) as IEnumerable)?.OfType&lt;object&gt;().Count() &gt; 0; 否则非空 IEnumerables 中的对象将被序列化为“{}”
    • 我认为我们也应该return base.CreateProperty(member, memberSerialization); 而不是最后一行的return property。并且 return property 仅当我们找到 IEnumerable 时。
    • 您应该使用GetProperty(property.UnderlyingName) 而不是GetProperty(property.PropertyName),以防您使用命名策略。
    【解决方案3】:

    Bryan 你是最不需要实例变量开销的方式,你需要捕获字段和成员实例,而且我不会运行计数操作,这需要枚举耗尽整个集合只需运行 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;
        }
    }
    

    【讨论】:

    • 我所有的数据模式都没有定义字段,所以我没有遇到这种情况。谢谢!出于某种特定原因,您是否需要 MemberTypes.Property 而不是 MemberTypes.Field 的空条件?另外,我不认为 string 对象会包含在我的检查中,但逻辑应该仍然适用,对吧?还是将它们的处理推迟到 Json.Net 更好?
    【解决方案4】:

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

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

    因为一个空列表也经常是空的。感谢您发布解决方案。 ATB。

    【讨论】:

    • 另一个选项是使用“NullValueHandling”属性:[JsonProperty("yourPropertyName", NullValueHandling = NullValueHandling.Ignore)]。这应该有助于减少对空检查的需求,这将稍微改进您的 if 检查。只是想我会提到它,因为您可能会觉得它很方便。
    • 在 ShouldSerialize 方法中你仍然需要检查空值,否则调用 ShouldSerialize 会抛出空值异常。即使序列化程序被编码为在检查 ShouldSerialize 方法时忽略异常,所引发的异常也会增加不必要的开销,从而影响性能。因此,通过首先检查 null,您可能获得的收益比失去的要多。
    猜你喜欢
    • 2016-09-10
    • 2015-02-16
    • 2013-08-30
    • 2011-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-10
    相关资源
    最近更新 更多