JSON.NET契约解决不适用于内部对象

时间:2016-11-21 20:21:13

标签: c# json.net

我正在尝试使用JSON.NET反序列化复杂的JSON结构:

{
  "a": 1,
  "b": "value",
  "c": {
    "d": "hello"
  }
}

我需要将值转换器应用于c属性。

我正在使用合同解析器,如下所示:

public class CustomPropertyResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        IList<JsonProperty> props = base.CreateProperties(type, memberSerialization);

        foreach (JsonProperty prop in props.Where(p => p.PropertyType == typeof(string)))
        {
            PropertyInfo pi = type.GetProperty(prop.UnderlyingName);
            if (pi != null && pi.GetCustomAttribute(typeof(MyCustomAttribute), true) != null)
            {
                prop.ValueProvider = new MyStringValueProvider(pi);
            }
        }

        return props;
    }

如果需要应用值提供程序的属性位于顶层(如b),则此方法有效,但它不适用于嵌套值(d)。根本没有为嵌套对象调用CreateProperties

如何确保合同解析器也适用于嵌套对象?

编辑:这是一个完整的最小复制品,应该打印“你好”和“世界”,但打印“olleh”和“dlrow”代替:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

namespace JsonNetStackOverflow
{
    public class Program
    {
        public static void Main(string[] args)
        {
            string json = "{'a':'olleh', 'b': { 'c': 'dlrow' } }";

            JsonSerializerSettings settings = new JsonSerializerSettings()
            {
                ContractResolver = new ReversePropertyResolver()
            };

            ClassA result = JsonConvert.DeserializeObject<ClassA>(json, settings);

            Console.WriteLine(result.A);
            Console.WriteLine(result.B.C);

            Console.ReadLine();
        }
    }

    public class ReverseAttribute : Attribute
    { }

    public class ReversePropertyResolver : DefaultContractResolver
    {
        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
        {
            IList<JsonProperty> props = base.CreateProperties(type, memberSerialization);

            foreach (JsonProperty prop in props.Where(p => p.PropertyType == typeof(string)))
            {
                PropertyInfo pi = type.GetProperty(prop.UnderlyingName);
                if (pi != null && pi.GetCustomAttribute(typeof(ReverseAttribute), true) != null)
                {
                    prop.ValueProvider = new ReverseValueProvider(pi);
                }
            }

            return props;
        }
    }

    public class ReverseValueProvider : IValueProvider
    {
        private readonly PropertyInfo targetProperty;

        public ReverseValueProvider(PropertyInfo targetProperty)
        {
            this.targetProperty = targetProperty;
        }

        public object GetValue(object target)
        {
            string value = (string)targetProperty.GetValue(target);
            return new string(value.Reverse().ToArray());
        }

        public void SetValue(object target, object value)
        {
            targetProperty.SetValue(target, new string(((string)value).Reverse().ToArray()));
        }
    }

    public class ClassA
    {
        public ClassA(string a, ClassB b)
        {
            this.A = a;
            this.B = b;
        }

        [Reverse]
        [JsonProperty("a")]
        public string A { get; set; }

        [JsonProperty("b")]
        public ClassB B { get; set; }
    }

    public class ClassB
    {
        public ClassB(string c)
        {
            this.C = c;
        }

        [Reverse]
        [JsonProperty("c")]
        public string C { get; set; }
    }
}

1 个答案:

答案 0 :(得分:1)

未调用ReverseValueProvider.SetValue()方法的原因是您的类型ClassAClassB正在使用参数化构造函数构造,并且传入的值是您想要的值反向:

public class ClassB
{
    public ClassB(string c)
    {
        // You would like c to be reversed but it's not since it's never set via its setter.
        this.C = c;
    }

    [Reverse]
    [JsonProperty("c")]
    public string C { get; set; }
}

因为Json.NET通过构造函数填充这些值,所以它永远不会调用相应的属性setter,并且字符串永远不会被反转。

为确保调用setter(因此字符串被反转),您可以向类型添加私有无参数构造函数,并使用[JsonConstructor]标记它们:

public class ClassA
{
    [JsonConstructor]
    ClassA() {}

    public ClassA(string a, ClassB b)
    {
        this.A = a;
        this.B = b;
    }

    [Reverse]
    [JsonProperty("a")]
    public string A { get; set; }

    [JsonProperty("b")]
    public ClassB B { get; set; }
}

public class ClassB
{
    [JsonConstructor]
    ClassB() { }

    public ClassB(string c)
    {
        this.C = c;
    }

    [Reverse]
    [JsonProperty("c")]
    public string C { get; set; }
}