2015-01-26 8 views
25

Vorrei deserializzare un oggetto System.Security.Claims.Claim serializzato nel seguente modo:Come scegliere a livello di programmazione un costruttore durante la deserializzazione?

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

quello che ottengo è una JsonSerializationException:

in grado di trovare un costruttore da utilizzare per il tipo System.Security. Claims.Claim. Una classe deve avere un costruttore predefinito , un costruttore con argomenti o un costruttore contrassegnato con con l'attributo JsonConstructor.

Dopo alcune indagini ho finalmente capito il significato di uno nel messaggio di cui sopra: il deserializzatore JSON non riesce a trovare il costruttore destra come ci sono - nel caso del tipo Claim - più costruttori con argomenti (sebbene esista un costruttore con argomenti che corrispondono esattamente alle proprietà precedenti).

C'è un modo per dire al deserializzatore quale costruttore scegliere senza aggiungere l'attributo JsonConstructor a quel tipo mscorlib?

Daniel Halan ha risolto questo problema con un patch to Json.NET a few years ago. C'è un modo per risolvere questo senza modificare Json.NET in questi giorni?

+0

Potrebbe pubblicare il tuo codice + costruttori? – Bauss

+3

Purtroppo non ho avuto il tempo di affrontarlo ora, ma puoi scavalcare e implementare il tuo 'JsonConverter'. [Questa risposta precedente] (http: // StackOverflow.it/questions/27311635/how-do-i-parse-a-json-string-to-ac-sharp-object-using-inheritance-polymorphis/27313288 # 27313288) potrebbe aiutarti a iniziare, ma dovrai prendilo un po 'oltre e leggi i campi json prima di chiamare il costruttore –

+0

Grazie per il link alla tua risposta precedente con il tuo 'JsonCreationConverter '! – CodeFox

risposta

28

Se non è possibile aggiungere un attributo [JsonConstructor] alla classe di destinazione (perché non si possiede il codice), la soluzione usuale è di creare un numero personalizzato JsonConverter come suggerito da @James Thorpe nei commenti. È piuttosto semplice. È possibile caricare il JSON in un JObject, quindi prelevare le singole proprietà al di fuori di esso per istanziare l'istanza Claim. Ecco il codice si avrebbe bisogno:

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

Per utilizzare il convertitore, è sufficiente passare un'istanza al JsonConvert.DeserializeObject<T>() chiamata di metodo:

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

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

+0

Felice che qualcuno abbia avuto il tempo di rispondere correttamente :) –

+0

Esattamente quello che stavo cercando! (E grazie per aver corretto lo spazio dei nomi errato nella mia domanda! ;-)) – CodeFox

+1

@CodeFox Nessun problema; felice di poterti aiutare! –

8

Un altro approccio, che lavorerà per classi non sigillate, la sottoclasse, ma con solo il costruttore che ti interessa:

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

È quindi possibile deserializzare questo oggetto senza classi di supporto e quindi trattarlo come tipo di base.

Claim claim = JsonConvert.DeserializeObject<MyClaim>(json); 

Per le classi sigillate, si potrebbe prendere questo approccio (finta per un secondo che Claim è sigillato):

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; 
+0

Grazie per aver condiviso i tuoi approcci alternativi! Credo che ci siano situazioni in cui sono più adatti di un convertitore personalizzato. – CodeFox

Problemi correlati