2014-07-09 6 views
17

Ich werde das vorweg bringen, indem ich sage, dass ich weiß, was das Problem ist, ich weiß einfach nicht, wie man es löst. Ich kommuniziere mit einer .NET SOA Datenschicht, die Daten als JSON zurückgibt. Eine solche Methode gibt ein Objekt zurück, das mehrere Sammlungen enthält. Das Objekt sieht grundsätzlich wie folgt:Typ ist eine Schnittstelle oder abstrakte Klasse und kann nicht instanziiert werden

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

Meine Klasse, die dieses Objekt repräsentiert sieht wie folgt aus:

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

Dieser Konstruktor hat gut funktioniert, bis die cogCollection ein Kind Sammlung hinzugefügt, und jetzt bekomme ich die oben Error. Eine konkrete cog Klasse sieht wie folgt aus:

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

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

Ich will nicht die Sammlung konkreter ändern, weil ich IoC verwende. Da ich IoC verwende, möchte ich wirklich von der Notwendigkeit der JsonConstructors, die konkrete Parameter nehmen, wegkommen, aber ich habe keinen Weg gefunden, dies zu tun. Jeder Rat würde sehr geschätzt werden!

Update:

Yuval Itzchakov Vorschlag, diese Frage ist wahrscheinlich ein Duplikat etwas wahr ist (wie es scheint). In dem Post, auf den verwiesen wird, bietet eine der Antworten auf der Seite dieselbe Lösung, die hier bereitgestellt wurde. Ich habe diese Antwort nicht bemerkt, da die Frage des OP anders war als die, die ich hier hatte. Mein Fehler.

------- meine Lösung --------

Wie gesagt: Matt-Lösung ein wenig Arbeit nahm, aber ich habe etwas Setup, das funktioniert für mich. Das einzige, was ich nicht über seine erste Lösung mögen, waren Zeilen wie diese:

return objectType == typeof(ICog); 

diesem Muster Nachfolgend finden Sie eine JsonConverter für jeden abstrakten Typ haben müssten, die Sie über den Draht zu erhalten. Dies ist weniger als ideal in meiner Situation, so habe ich eine generische JsonConverter als solche:

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

Dann, kurz bevor ich meine Daten deserialisiert, das tue ich etwas wie folgt aus:

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

Protip: Wenn Sie Verwenden Sie Resharper, (JsonConverter) gibt Ihnen in diesem Szenario eine Warnung für verdächtige Änderungen.

Hoffentlich findet jemand anderes diese nützliche unten auf der Straße!

+0

Versuchen 'TypeNameHandling.All' der' JsonSerializerSettings'-Klasse und in das json Serializer –

+0

möglich Duplikat [Casting-Schnittstellen für die Deserialisierung in JSON.NET] vorbei (http://stackoverflow.com/questions/5780888/ casting-interfaces-für-deserialization-in-json-net) –

+0

mögliches Duplikat von [Verwendung von Json.NET Konvertern zur Deserialisierung von Eigenschaften] (http://stackoverflow.com/questions/2254872/using-json-net-converters-to -deserialize-Eigenschaften) – nawfal

Antwort

22

Sie müssen Json.Net einen benutzerdefinierten Serializer zur Verfügung stellen, um zu erfahren, wie mit den untergeordneten Cogs verfahren wird. Zum Beispiel:

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

Ihre CogConverter wird von JsonConverter erben müssen und angeben, dass es CanConvert Ihre ICog Schnittstelle. Vielleicht etwas entlang der Linien von:

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

ich Registrierung empfehlen würde Ihre JsonSerializerSettings mit Ihrem IoC-Container in diesem Fall.Sie können auch in Betracht ziehen, CogConverter Zugriff auf den Container zu geben, wenn der Serializer nicht für die eigentliche Erstellung des Cog selbst verantwortlich sein kann; Das kommt auf Ihre spezielle Architektur an.

bearbeiten

Bei der weiteren Lektüre scheint es, wie Sie gezielt suchen können, wie die IoC ICog für Bevölkerung erstellt wird. Ich verwende die folgenden als Teil meiner ReadJson:

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; 

Auf diese Weise können Sie ein beliebiges Objekt verwenden und aus dem ursprünglichen JSON bevölkern, benutzerdefinierte Typen verwenden, wie Sie in Ihrem DetermineConcreteType Methode wollen.

+1

Danke, Matt. Ich gebe das eine Chance und sehe, ob es funktioniert. – dparsons

+0

Mit ein bisschen Arbeit war das die richtige Antwort. Ich bin allerdings neugierig, was macht Ihre 'DetermineConcreteType'-Methode? – dparsons

+0

In unserem sehen wir uns ein paar Eigenschaften an, um einen korrekten Subtyp zu bestimmen (unsere Schnittstellen sind nicht 1: 1 mit Implementierungen) - ein allgemeiner, den ich mir vorstellen kann, wäre ein Drop-down, das die Zahlungsmethode anzeigt (Kreditkarte vs Bank usw.) - jede hat unterschiedliche Felder, aber der Wert des Dropdown-Feldes kann diesen Typ anzeigen. Wenn Sie Code haben, den Sie als Teil der Antwort freigeben möchten, würde ich gerne meine Version aktualisieren, um eine vollständigere Lösung zu enthalten. –

Verwandte Themen