如何在反序列化过程中以编程方式选择构造函数?

我想反序列化以下列方式序列化的System.Security.Claims.Claim对象:

 { "Issuer" : "LOCAL AUTHORITY", "OriginalIssuer" : "LOCAL AUTHORITY", "Type" : "http://my.org/ws/2015/01/identity/claims/mytype", "Value" : "myvalue", "ValueType" : "http://www.w3.org/2001/XMLSchema#string" } 

我得到的是JsonSerializationException

无法找到用于System.Security.Claims.Claim类型的构造函数。 一个类应该有一个默认的构造函数,一个带参数的构造函数或一个用JsonConstructor属性标记的构造函数。

经过一些调查后,我终于理解了上面消息中一个的含义:JSON反序列化器找不到正确的构造函数 – 在Claim类型的情况下 – 带有参数的多个构造函数 (尽管存在一个构造函数,其参数完全匹配以上属性)。

有没有办法告诉反序列化器选择哪个构造函数而不将JsonConstructor属性添加到该mscorlib类型?

几年前, Daniel Halan用Json.NET的补丁解决了这个问题。 有没有办法在不修改Json.NET的情况下解决这个问题?

如果无法将[JsonConstructor]属性添加到目标类(因为您不拥有代码),那么通常的解决方法是创建一个自定义JsonConverter如@James Thorpe在评论中所建议的那样。 这很简单。 您可以将JSON加载到JObject ,然后JObject选择单个属性以实例化您的Claim实例。 这是您需要的代码:

 class ClaimConverter : JsonConverter { public override bool CanConvert(Type objectType) { return (objectType == typeof(System.Security.Claims.Claim)); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { JObject jo = JObject.Load(reader); string type = (string)jo["Type"]; string value = (string)jo["Value"]; string valueType = (string)jo["ValueType"]; string issuer = (string)jo["Issuer"]; string originalIssuer = (string)jo["OriginalIssuer"]; return new Claim(type, value, valueType, issuer, originalIssuer); } public override bool CanWrite { get { return false; } } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } } 

要使用转换器,只需将其实例传递给JsonConvert.DeserializeObject()方法调用:

 Claim claim = JsonConvert.DeserializeObject(json, new ClaimConverter()); 

小提琴: https : //dotnetfiddle.net/7LjgGR

另一种方法,至少适用于非密封类,是子类化,但只有你感兴趣的构造函数:

 class MyClaim : Claim { public MyClaim(string type, string value, string valueType, string issuer, string originalIssuer): base(type, value, valueType, issuer, originalIssuer){} } 

然后,您可以在没有帮助程序类的情况下反序列化到此对象,然后将其视为基本类型。

 Claim claim = JsonConvert.DeserializeObject(json); 

对于密封类,您可以采用这种方法(假装一秒钟Claim密封):

 class MyClaim { private Claim _claim; public MyClaim(string type, string value, string valueType, string issuer, string originalIssuer) { _claim = new Claim(type, value, valueType, issuer, originalIssuer); } public Claim Value { get { return _claim; } } } Claim claim = JsonConvert.DeserializeObject(json).Value;