c# - 解析的 JSON.NET 契约(Contract)不适用于内部对象

标签 c# json.net

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

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

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

我正在使用如下所示的契约(Contract)解析器:

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 根本不会为嵌套对象调用。

如何确保契约(Contract)解析器也应用于嵌套对象?

编辑:这是一个完整的最小重现,应该打印“hello”和“world”,但打印“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; }
    }
}

最佳答案

未调用 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 被调用(因此字符串被反转),您可以将私有(private)无参数构造函数添加到您的类型中,并用 [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; }
}

关于c# - 解析的 JSON.NET 契约(Contract)不适用于内部对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40728650/

相关文章:

c# - 如何干净地反序列化字符串值包含在同名对象中的 JSON

c# - 如何在 dotnet core 测试项目中使用用户 secret

c# - 将 XElement 转换为不带 JSON 的 JObject - 或 - 为空元素配置 SerializeXNode

c# - 如何将日期对象添加到 C# 列表?

c# - 为什么复制流然后使用 BinaryFormatter 反序列化比仅仅反序列化更快

c# - 为什么 Newtonsoft.Json 将 byte[] 序列化为 base64 而不是 sbyte[]?

c# - DeserializeObject<T> 返回具有空/默认值的对象

xml - 使用 Newtonsoft 将 Json 值转换为 Integer

c# - Unity Engine - 通过碰撞实例化预制件

c# - 不能在FB post发图片,这个status update好像是空白。请写一些东西或附上链接或照片以更新您的状态