64

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

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

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

    }
}

目前的结果是: { "Identity": 123, "Numbers": [] }

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

也就是说,我想跳过任何列表、集合、数组或此类为空的内容。

4

4 回答 4

86

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

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

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

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

于 2012-11-05T18:28:25.203 回答
25

关于 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);
于 2018-09-27T01:57:41.000 回答
7

布莱恩,你是最不需要实例变量开销的方式,你需要捕获字段和成员实例,而且我不会运行计数操作,这需要枚举耗尽整个集合,你可以简单地运行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;
    }
}
于 2019-01-04T06:05:16.163 回答
3

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

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

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

于 2013-07-02T13:44:20.027 回答