Je souhaite désérialiser un objet System.Security.Claims.Claim
sérialisé de la manière suivante:
{
"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"
}
Ce que je reçois est une JsonSerializationException
:
Impossible de trouver un constructeur à utiliser pour le type System.Security.Claims.Claim. Une classe doit avoir un constructeur par défaut, un constructeur avec arguments ou un constructeur marqué avec l'attribut JsonConstructor.
Après quelques recherches, je comprends enfin la signification de un dans le message ci-dessus: Le désérialiseur JSON ne peut pas trouver le constructeur approprié, car il existe - dans le cas de Claim
type - plusieurs constructeurs avec arguments (bien qu'il existe un constructeur avec des arguments correspondant exactement aux propriétés ci-dessus).
Existe-t-il un moyen de dire au désérialiseur quel constructeur choisir sans ajouter l'attribut JsonConstructor
à ce type mscorlib?
Daniel Halan a résolu ce problème avec un patch vers Json.NET il y a quelques années . Y at-il un moyen de résoudre ce problème sans modifier Json.NET ces jours-ci?
S'il n'est pas possible d'ajouter un attribut [JsonConstructor]
à la classe cible (car vous ne possédez pas le code), la solution habituelle consiste à créer une variable JsonConverter
personnalisée, comme l'a suggéré @James Thorpe dans les commentaires. C'est assez simple. Vous pouvez charger le JSON dans une JObject
, puis en extraire les propriétés individuelles pour instancier votre instance Claim
. Voici le code dont vous auriez besoin:
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();
}
}
Pour utiliser le convertisseur, passez simplement une instance de celui-ci à l'appel à la méthode JsonConvert.DeserializeObject<T>()
:
Claim claim = JsonConvert.DeserializeObject<Claim>(json, new ClaimConverter());
Fiddle: https://dotnetfiddle.net/7LjgGR
Une autre approche, qui fonctionnera au moins pour les classes non scellées, consiste à la sous-classer, mais avec uniquement le constructeur qui vous intéresse:
class MyClaim : Claim {
public MyClaim(string type, string value, string valueType, string issuer, string originalIssuer):
base(type, value, valueType, issuer, originalIssuer){}
}
Vous pouvez ensuite désérialiser cet objet sans classes auxiliaires, puis le traiter comme type de base.
Claim claim = JsonConvert.DeserializeObject<MyClaim>(json);
Pour les classes scellées, vous pouvez adopter cette approche (en prétendant pendant une seconde que Claim
est scellé):
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<MyClaim>(json).Value;
Une ClaimConverter
a été fournie avec IdentityServer4 .
Espace de noms: IdentityServer4.Stores.Serialization
Exemple d'utilisation:
JsonConvert.DeserializeObject<T>(value, new IdentityServer4.Stores.Serialization.ClaimConverter());