Json.NET从自动属性初始化程序获取默认值 [英] Json.NET get default values from auto-property initializer

查看:70
本文介绍了Json.NET从自动属性初始化程序获取默认值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想减少使用默认值生成的字符串 Json.NET .

I want to reduce the string Json.NET generates by using default values.

我的属性之一是:

public string Name { get; set; } = "Jennifer";

我已经使用了自动属性初始化程序,因此如果为空,则填充该字符串.

I already use the auto-property initializer so the string gets populated if empty.

在使用Json.NET进行序列化时,我使用DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate,因此只有更改后的属性才能保留.

When serializing with Json.NET I use DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate so only changed properties get persisted.

我知道我可以像这样使用DefaultValueAttribut:

I know that I can use the DefaultValueAttribut like this:

[DefaultValue("Jennifer")]
public string Name { get; set; } = "Jennifer";

但是我想知道是否可以跳过此属性,并在序列化时使用自动属性初始值作为默认值.

But I am wondering if I can skip this attribute and use auto-property initial values as default value when serializing.

推荐答案

是的,您可以使用自定义的ContractResolver来完成此操作,例如以下示例.它的工作方式是为每个Type创建一个引用实例(假定Type是一个类,并且具有默认的构造函数),然后在每个属性上设置一个ShouldSerialize谓词,以根据引用检查该属性的当前值实例.如果它们匹配,则ShouldSerialize返回false,并且该属性未序列化.

Yes, you can do this using a custom ContractResolver such as the one below. It works by creating a reference instance for each Type (assuming the Type is a class and has a default constructor available) and then setting a ShouldSerialize predicate on each property which checks the current value of the property against the reference instance. If they match, then ShouldSerialize returns false and the property is not serialized.

class CustomResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        IList<JsonProperty> props = base.CreateProperties(type, memberSerialization);
        if (type.IsClass)
        {
            ConstructorInfo ctor = type.GetConstructor(Type.EmptyTypes);
            if (ctor != null)
            {
                object referenceInstance = ctor.Invoke(null);
                foreach (JsonProperty prop in props.Where(p => p.Readable))
                {
                    prop.ShouldSerialize = instance =>
                    {
                        object val = prop.ValueProvider.GetValue(instance);
                        object refVal = prop.ValueProvider.GetValue(referenceInstance);
                        return !ObjectEquals(val, refVal);
                    };
                }
            }
        }
        return props;
    }

    private bool ObjectEquals(object a, object b)
    {
        if (ReferenceEquals(a, b)) return true;
        if (a == null || b == null) return false;
        if (a is IEnumerable && b is IEnumerable && !(a is string) && !(b is string))
            return EnumerableEquals((IEnumerable)a, (IEnumerable)b);
        return a.Equals(b);
    }

    private bool EnumerableEquals(IEnumerable a, IEnumerable b)
    {
        IEnumerator enumeratorA = a.GetEnumerator();
        IEnumerator enumeratorB = b.GetEnumerator();
        bool hasNextA = enumeratorA.MoveNext();
        bool hasNextB = enumeratorB.MoveNext();
        while (hasNextA && hasNextB)
        {
            if (!ObjectEquals(enumeratorA.Current, enumeratorB.Current)) return false;
            hasNextA = enumeratorA.MoveNext();
            hasNextB = enumeratorB.MoveNext();
        }
        return !hasNextA && !hasNextB;
    }
}

要使用解析器,您需要将其添加到JsonSerializerSettings并将设置传递给SerializeObject方法,如下所示:

To use the resolver you need to add it to the JsonSerializerSettings and pass the settings to the SerializeObject method like this:

JsonSerializerSettings settings = new JsonSerializerSettings
{
    ContractResolver = new CustomResolver(),
};
string json = JsonConvert.SerializeObject(yourObject, settings);

这是一个有效的演示: https://dotnetfiddle.net/K1WbSP

Here is a working demo: https://dotnetfiddle.net/K1WbSP

有关此解决方案的一些说明:

Some notes on this solution:

  • 使用此解析器具有优于DefaultValue属性的功能优势,因为它可以处理复杂的默认值,例如ListsDictionaries和子对象(前提是您已在子对象上正确实现了Equals方法)类).属性只能处理简单的常量表达式(例如字符串,枚举和其他基元).但是,如果您所需要的只是简单的默认值,请注意,此解析器的性能可能会比仅使用属性稍差一些,因为它需要使用其他反射来实例化引用对象并比较所有属性值.因此,需要进行一些权衡.如果您的JSON很小,那么您可能不会注意到其中的区别.但是,如果您的JSON很大,那么您可能需要做一些基准测试.

  • Using this resolver has a functional advantage over DefaultValue attributes in that it can handle complex defaults like Lists, Dictionaries and child objects (provided you have correctly implemented the Equals method on the child classes). Attributes can only handle simple constant expressions (e.g. strings, enums and other primitives). However, if all you need is simple defaults, be aware that this resolver will probably perform slightly worse than just using the attributes because it needs to use additional reflection to instantiate the reference objects and compare all the property values. So there is a little bit of a tradeoff. If your JSON is small, you probably won't notice a difference. But if your JSON is large, then you may want do some benchmarks.

您可能希望实现类级别的退出机制(例如,解析器查找的自定义属性,或者可能传递给解析器的类名列表),以防万一要做的地方,是要为某些类而不是其他类序列化默认值.

You may want to implement a class-level opt-out mechanism (i.e. a custom attribute that the resolver looks for, or maybe a list of class names that you pass to the resolver) in case you run into situations where you do want the default values to be serialized for certain classes but not others.

这篇关于Json.NET从自动属性初始化程序获取默认值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆