2014-07-09 13 views
17

Premetto che so qual è il problema, ma non so come risolverlo. Sto comunicando con un livello dati SOA .NET che restituisce i dati come JSON. Uno di questi metodi restituisce un oggetto che contiene diverse raccolte al suo interno. L'oggetto si presenta sostanzialmente in questo modo:Il tipo è un'interfaccia o classe astratta e non può essere istanziato

{ 
    "Name":"foo", 
    "widgetCollection":[{"name","foo"}, {"name","foo"},], 
    "cogCollection": [{"name","foo"}, {"childCogs",<<new collection>>},], 
} 

mia classe che rappresenta l'oggetto si presenta così:

public class SuperWidget : IWidget 
{ 
    public string Name { get; set; } 

    public ICollection<IWidget> WidgetCollection { get; set; } 
    public ICollection<ICog> CogCollection { get; set; } 

    public SuperWidget() 
    { 
    } 

    [JsonConstructor] 
    public SuperWidget(IEnumerable<Widget> widgets, IEnumerable<Cog> cogs) 
    { 
     WidgetCollection = new Collection<IWidget>(); 
     CogCollection = new Collection<ICog>(); 

     foreach (var w in widgets) 
     { 
      WidgetCollection.Add(w); 
     } 
     foreach (var c in cogs) 
     { 
      CogCollection.Add(c); 
     } 
    } 
} 

Questo costruttore ha funzionato bene fino a quando il cogCollection aggiunto una collezione bambino, e ora sto ottenendo quanto sopra errore. Una classe COG concreta si presenta così:

[Serializable] 
public class Cog : ICog 
{ 
    public string name { get; set; } 

    public ICollection<ICog> childCogs { get; set; }   
} 

Io non voglio cambiare la collezione ad un tipo concreto perché sto usando CIO. Perché sto usando IoC mi piacerebbe davvero allontanarmi dalla necessità di avere i JsonConstructors che prendono parametri concreti, ma non ho trovato un modo per farlo. Qualsiasi consiglio sarebbe molto apprezzato!

Aggiornamento: il suggerimento di

Yuval Itzchakov che questa domanda è probabilmente un duplicato è un po 'vero (a quanto pare). Nel post referenziato, una delle risposte nella pagina fornisce la stessa soluzione che è stata fornita qui. Non ho notato quella risposta, dal momento che la domanda dell'OP era diversa da quella che avevo qui. Errore mio.

------- -------- mia soluzione

Come ho già detto: la soluzione di Matt ha preso un po 'di lavoro ma ho ottenuto l'installazione qualcosa che funziona per me. L'unica cosa che non mi piace per la sua soluzione iniziale, erano righe come queste:

return objectType == typeof(ICog); 

Seguendo questo schema si avrebbe bisogno di avere un JsonConverter per ogni tipo astratto che si riceve sul filo. Questo è proprio l'ideale nella mia situazione, così ho creato un JsonConverter generica in quanto tale:

public class GenericJsonConverter<T>: JsonConverter, IBaseJsonConverter<T> 
{ 
    private readonly IUnityContainer Container; 
    public TalonJsonConverter(IUnityContainer container) 
    { 
     Container = container; 
    } 

    public override bool CanConvert(Type objectType) 
    { 
     return objectType == typeof(T); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     var target = serializer.Deserialize<Newtonsoft.Json.Linq.JObject>(reader); 
     var result = Container.Resolve<T>(); 
     serializer.Populate(target.CreateReader(), result); 
     return result; 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     serializer.Serialize(writer, value); 
    } 
} 

Poi, proprio prima che io deserializzare i miei dati, faccio qualcosa di simile:

var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }; 
settings.Converters.Add((JsonConverter)Container.Resolve<IBaseJsonConverter<ICog>>()); 

Protip: Se usa Resharper, (JsonConverter) ti darà un cast cast sospettoso in questo scenario.

Speriamo che qualcun altro trovi utile questo in fondo alla strada!

+0

Provare a usare 'TypeNameHandling.All' della classe' JsonSerializerSettings' e passarlo al serializzatore json –

+0

possibile duplicato di [Interfacce di fusione per la deserializzazione in JSON.NET] (http://stackoverflow.com/questions/5780888/ casting-interface-per-deserialization-in-json-net) –

+0

possibile duplicato di [Utilizzo dei convertitori Json.NET per deserializzare le proprietà] (http://stackoverflow.com/questions/2254872/using-json-net-converters-to -deserialize-properties) – nawfal

risposta

22

È necessario fornire un serializzatore personalizzato a Json.Net per comunicare come gestire gli ingranaggi figlio. Per esempio:

var settings = new JsonSerializerSettings(); 
settings.Converters.Add(new CogConverter()); 

tuo CogConverter dovranno ereditare da JsonConverter e specificare che non CanConvert l'interfaccia ICog. Forse qualcosa sulla falsariga di:

public class CogConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return objectType == typeof(ICog); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     return serializer.Deserialize(reader, typeof(Cog)); 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     serializer.Serialize(writer, value); 
    } 
} 

avrei consiglia di registrare il JsonSerializerSettings con il contenitore CIO in questo caso.Potresti considerare di dare l'accesso al contenitore anche a CogConverter, se il serializzatore non può essere responsabile della costruzione effettiva dello stesso Cog; tutto dipende dalla tua particolare architettura.

Modifica

Su ulteriore lettura, sembra come si potrebbe essere alla ricerca di specificamente come utilizzare il CIO ha creato ICog per la popolazione. Sto utilizzando il seguente come parte del mio ReadJson: si

var target = serializer.Deserialize<Newtonsoft.Json.Linq.JObject>(reader); 
var objectType = DetermineConcreteType(target); 
var result = iocContainer.Resolve(objectType); 
serializer.Populate(target.CreateReader(), result); 
return result; 

Questo permette di utilizzare qualsiasi oggetto e popolare dall'originale JSON, utilizzando tipi personalizzati a piacere all'interno del vostro metodo di DetermineConcreteType.

+1

Grazie, Matt. Darò questo un colpo in un po 'e vedere se funziona. – dparsons

+0

Con un po 'di lavoro, questa era la risposta corretta. Sono curioso però, cosa fa il tuo metodo 'DetermineConcreteType'? – dparsons

+0

Nel nostro caso, osserviamo alcune proprietà su di esso per determinare un sottotipo corretto (le nostre interfacce non sono 1: 1 con le implementazioni) - una generica che posso pensare sarebbe un elenco a discesa che indica il metodo di pagamento (carta di credito vs .bank, ecc.) - ognuno ha campi diversi, ma il valore del drop down può indicare quel tipo. Se hai il codice che vuoi condividere come parte della risposta, aggiornerei felicemente il mio per includere una soluzione più completa. –

Problemi correlati