【问题标题】:Deserializing public property with non-public setter in json.net在 json.net 中使用非公共设置器反序列化公共属性
【发布时间】:2018-01-23 15:13:17
【问题描述】:

假设我有以下课程 -

public class A 
{        
   public int P1 { get; internal set; }
}

使用 json.net,我可以使用 P1 属性序列化类型。但是,在反序列化期间,不设置 P1。在不修改 A 类的情况下,是否有一种构建方式来处理这个问题?就我而言,我正在使用来自不同程序集的类并且无法修改它。

【问题讨论】:

标签: json.net deserialization


【解决方案1】:

是的,您可以使用自定义 ContractResolver 使内部属性可写入 Json.Net。这是您需要的代码:

class CustomResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty prop = base.CreateProperty(member, memberSerialization);

        if (member.DeclaringType == typeof(A) && prop.PropertyName == "P1")
        {
            prop.Writable = true;
        }

        return prop;
    }
}

要使用解析器,请创建JsonSerializerSettings 的实例并将其ContractResolver 属性设置为自定义解析器的新实例。然后,将设置传递给JsonConvert.DeserializeObject<T>()

演示:

class Program
{
    static void Main(string[] args)
    {
        string json = @"{ ""P1"" : ""42"" }";

        JsonSerializerSettings settings = new JsonSerializerSettings();
        settings.ContractResolver = new CustomResolver();

        A a = JsonConvert.DeserializeObject<A>(json, settings);

        Console.WriteLine(a.P1);
    }
}

输出:

42

小提琴:https://dotnetfiddle.net/1fw2lC

【讨论】:

  • BrianRogers 遵循您的示例,但删除了检查类型和属性的条件,因为希望所有内容都是可写的。有效。谢谢:)
【解决方案2】:

经过一些实验,我发现如果您使用以下方式装饰您的属性,则该属性可以正确反序列化:

[JsonProperty(DefaultValueHandling = DefaultValueHandling.Include)] 

申请原题中的班级:

public class A 
{
   [JsonProperty(DefaultValueHandling = DefaultValueHandling.Include)]        
   public int P1 { get; internal set; }
}

【讨论】:

  • 注意这一点,根据文档,这在反序列化时应该没有效果newtonsoft.com/json/help/html/…
  • 其实和DefaultValueHandling没有关系,你可以用[JsonProperty]装饰一下,效果一样。
【解决方案3】:

这是我在更一般情况下处理的解决方案:

class CustomResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty result = base.CreateProperty(member, memberSerialization);

        var propInfo = member as PropertyInfo;
        result.Writable |= propInfo != null 
             && propInfo.CanWrite
             && !propInfo.IsPrivate;

        return result;
    }
}

使用 CInternalSetter 类:

class CInternalSetter
{
    public CInternalSetter()
    {
        LoggedEmployeeId3 = 10;
    }
    public int LoggedEmployeeId { get; set; }
    public int LoggedEmployeeId2 { get; internal set; }
    public int LoggedEmployeeId3 { get; }
    public override string ToString()
    {
        return JsonConvert.SerializeObject(this, Formatting.Indented);
    }
}

然后用这个类对其进行测试:

JsonSerializerSettings settings = new JsonSerializerSettings();
settings.ContractResolver = new CustomResolver();

var c = new CInternalSetter()
{
    LoggedEmployeeId = 1,
    LoggedEmployeeId2 = 2
};

var cString = JsonConvert.SerializeObject(c);

Console.WriteLine(cString);
Console.WriteLine(JsonConvert.DeserializeObject<CInternalSetter>(cString).ToString());
Console.WriteLine("-------------------------------------------");
Console.WriteLine(JsonConvert.DeserializeObject<CInternalSetter>(cString, settings).ToString());

【讨论】:

  • 这个通用实现正是我所需要的,但我认为 CreatePropertyMethod 中有一个错字:propInfo.IsPrivate 应该是 propInfo.SetMethod.IsPrivate
猜你喜欢
  • 1970-01-01
  • 2018-08-03
  • 2018-03-06
  • 1970-01-01
  • 1970-01-01
  • 2011-04-27
  • 2012-08-29
  • 1970-01-01
  • 2015-12-06
相关资源
最近更新 更多