¿Cómo elegir programáticamente un constructor durante la deserialización?

Me gustaría deserializar un objeto System.Security.Claims.Claim serializado de la siguiente manera:

 { "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" } 

Lo que obtengo es una JsonSerializationException :

No se puede encontrar un constructor para usar para el tipo System.Security.Claims.Claim. Una clase debe tener un constructor predeterminado, un constructor con argumentos o un constructor marcado con el atributo JsonConstructor.

Después de investigar un poco, finalmente entiendo el significado de uno en el mensaje anterior: El deserializador JSON no puede encontrar el constructor correcto, ya que hay – en el caso del tipo Claimmúltiples constructores con argumentos (aunque existe un constructor con argumentos que coinciden exactamente propiedades anteriores).

¿Hay alguna manera de decirle al deserializador qué constructor elegir sin agregar el atributo JsonConstructor a ese tipo de mscorlib?

Daniel Halan resolvió este problema con un parche en Json.NET hace algunos años . ¿Hay alguna manera de resolver esto sin modificar Json.NET en estos días?

Si no es posible agregar un atributo [JsonConstructor] a la clase objective (porque usted no posee el código), entonces la solución habitual es crear un JsonConverter personalizado como lo sugirió @James Thorpe en los comentarios. Es bastante sencillo. Puede cargar el JSON en un JObject y luego JObject las propiedades individuales para crear una instancia de su instancia de Claim . Aquí está el código que necesitaría:

 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(); } } 

Para usar el convertidor, simplemente pase una instancia de él a la JsonConvert.DeserializeObject() al JsonConvert.DeserializeObject() :

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

Fiddle: https://dotnetfiddle.net/7LjgGR

Otro enfoque, que funcionará para las clases no selladas al menos, es subclasificarlo, pero con solo el constructor que le interese:

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

Luego puede deserializar este objeto sin clases auxiliares y luego tratarlo como el tipo de base.

 Claim claim = JsonConvert.DeserializeObject(json); 

Para las clases selladas, puede tomar este enfoque (pretendiendo por un segundo que el Claim está sellado):

 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;