代码之家  ›  专栏  ›  技术社区  ›  Jenny

Json.NET从自动属性初始值设定项获取默认值

  •  2
  • Jenny  · 技术社区  · 6 年前

    我想减少绳子的长度 Json.NET 使用默认值生成。

    我的一个财产是:

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

    我已经使用了auto属性初始值设定项,因此如果字符串为空,它将被填充。

    当使用Json.NET序列化时,我使用 DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate 因此,只有更改的属性才会被持久化。

    我知道我可以用 DefaultValueAttribut 这样地:

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

    但是我想知道是否可以跳过这个属性,并在序列化时使用auto property初始值作为默认值。

    1 回复  |  直到 6 年前
        1
  •  2
  •   Brian Rogers    6 年前

    是的,您可以使用自定义 ContractResolver 比如下面的那个。它通过为每个 Type (假设 类型 是一个类,并且有一个可用的默认构造函数),然后设置 ShouldSerialize 每个属性上的谓词,这些属性根据引用实例检查属性的当前值。如果匹配,那么 应序列化 返回false且属性未序列化。

    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 方法如下:

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

    下面是一个工作演示: https://dotnetfiddle.net/K1WbSP

    关于此解决方案的一些注意事项:

    • 使用此解析器比 DefaultValue 属性,它可以处理复杂的默认值,如 Lists , Dictionaries 和子对象(只要您正确地实现了 Equals 子类上的方法)。属性只能处理简单的常量表达式(例如字符串、枚举和其他原语)。但是,如果您所需要的只是简单的默认值,请注意,此解析器的性能可能比仅使用属性稍差,因为它需要使用附加反射来实例化引用对象并比较所有属性值。所以有一点折衷。如果JSON很小,您可能不会注意到有什么不同。但是如果JSON很大,那么您可能需要做一些基准测试。

    • 如果遇到以下情况,您可能需要实现类级别的退出机制(即解析器查找的自定义属性,或传递给解析器的类名列表) 希望对某些类(而不是其他类)序列化默认值。