Ich versuche, einen Reader einzurichten, der JSON-Objekte von verschiedenen Websites aufnimmt (Think Information Scraping) und sie in C # -Objekte übersetzt. Ich verwende derzeit JSON.NET für den Deserialisierungsprozess. Das Problem, auf das ich stoße, ist, dass es nicht weiß, wie man Eigenschaften auf Schnittstellenebene in einer Klasse behandelt. Also etwas von der Natur:
public IThingy Thing
Wird den Fehler erzeugen:
Es konnte keine Instanz vom Typ IThingy erstellt werden. Typ ist eine Schnittstelle oder abstrakte Klasse und kann nicht instanziiert werden.
Es ist relativ wichtig, dass es sich um ein IThingy im Gegensatz zu einem Thingy handelt, da der Code, an dem ich arbeite, als vertraulich angesehen wird und Unit-Tests sehr wichtig sind. Das Verspotten von Objekten für Atomtestskripte ist mit vollwertigen Objekten wie Thingy nicht möglich. Sie müssen eine Schnittstelle sein.
Ich habe jetzt schon eine Weile über die Dokumentation von JSON.NET nachgedacht, und die Fragen, die ich auf dieser Website dazu finden konnte, stammen alle von vor über einem Jahr. Irgendeine Hilfe?
Wenn es darauf ankommt, ist meine App auch in .NET 4.0 geschrieben.
Antworten:
@SamualDavis bot eine großartige Lösung für eine verwandte Frage , die ich hier zusammenfassen werde.
Wenn Sie einen JSON-Stream in eine konkrete Klasse mit Schnittstelleneigenschaften deserialisieren müssen, können Sie die konkreten Klassen als Parameter in einen Konstruktor für die Klasse aufnehmen! Der NewtonSoft-Deserializer ist intelligent genug, um herauszufinden, dass er diese konkreten Klassen zum Deserialisieren der Eigenschaften verwenden muss.
Hier ist ein Beispiel:
quelle
[JsonConstructor]
Attribut markieren .(Von dieser Frage kopiert )
In Fällen, in denen ich keine Kontrolle über den eingehenden JSON hatte (und daher nicht sicherstellen kann, dass er eine $ type-Eigenschaft enthält), habe ich einen benutzerdefinierten Konverter geschrieben, mit dem Sie nur den konkreten Typ explizit angeben können:
Dies verwendet nur die Standard-Serializer-Implementierung von Json.Net, während der konkrete Typ explizit angegeben wird.
Eine Übersicht finden Sie in diesem Blogbeitrag . Der Quellcode ist unten:
quelle
ConcreteListTypeConverter<TInterface, TImplementation>
, um Klassenmitglieder vom Typ zu behandelnIList<TInterface>
.concreteTypeConverter
die Frage zu haben.ConcreteListTypeConverter<TInterface, TImplementation>
Implementierung veröffentlichen?Warum einen Konverter verwenden? Es gibt eine native Funktionalität
Newtonsoft.Json
, um genau dieses Problem zu lösen:Stellen Sie
TypeNameHandling
dasJsonSerializerSettings
in einTypeNameHandling.Auto
Dadurch wird jeder Typ in den JSON eingefügt, der nicht als konkrete Instanz eines Typs, sondern als Schnittstelle oder abstrakte Klasse gespeichert wird.
Stellen Sie sicher, dass Sie dieselben Einstellungen für die Serialisierung und Deserialisierung verwenden .
Ich habe es getestet und es funktioniert wie ein Zauber, auch mit Listen.
Suchergebnisse Web-Ergebnis mit Site-Links
⚠️ WARNUNG :
Verwenden Sie dies nur für JSON aus einer bekannten und vertrauenswürdigen Quelle. Benutzer snipsnipsnip richtig erwähnt, dass dies in der Tat eine Vunerability ist.
Weitere Informationen finden Sie unter CA2328 und SCS0028 .
Quelle und eine alternative manuelle Implementierung: Code Inside Blog
quelle
Um die Deserialisierung mehrerer Schnittstellenimplementierungen zu ermöglichen, können Sie JsonConverter verwenden, jedoch nicht über ein Attribut:
DTOJsonConverter ordnet jede Schnittstelle einer konkreten Implementierung zu:
DTOJsonConverter wird nur für den Deserializer benötigt. Der Serialisierungsprozess bleibt unverändert. Das Json-Objekt muss keine konkreten Typnamen einbetten.
Dieser SO-Beitrag bietet die gleiche Lösung einen Schritt weiter mit einem generischen JsonConverter.
quelle
FullName
s, wenn Sie nur Typen direkt vergleichen können?Verwenden Sie diese Klasse, um den abstrakten Typ dem realen Typ zuzuordnen:
... und wenn deserialisieren:
quelle
where TReal : TAbstract
um sicherzustellen, dass es in den Typ umgewandelt werden kannwhere TReal : class, TAbstract, new()
.Nicholas Westby bot eine großartige Lösung in einem großartigen Artikel .
Wenn Sie JSON in eine von vielen möglichen Klassen deserialisieren möchten, die eine solche Schnittstelle implementieren:
Sie können einen benutzerdefinierten JSON-Konverter verwenden:
Außerdem müssen Sie die Eigenschaft "Profession" mit einem JsonConverter-Attribut dekorieren, damit Sie wissen, dass Sie Ihren benutzerdefinierten Konverter verwenden können:
Und dann können Sie Ihre Klasse mit einem Interface besetzen:
quelle
Zwei Dinge, die Sie versuchen könnten:
Implementieren Sie ein Try / Parse-Modell:
Wenn Sie dies in Ihrem Objektmodell tun können, implementieren Sie eine konkrete Basisklasse zwischen IPerson und Ihren Blattobjekten und deserialisieren Sie diese.
Der erste kann möglicherweise zur Laufzeit fehlschlagen, der zweite erfordert Änderungen an Ihrem Objektmodell und homogenisiert die Ausgabe auf den kleinsten gemeinsamen Nenner.
quelle
Ich fand das nützlich. Du könntest auch.
Beispiel Verwendung
Benutzerdefinierter Erstellungskonverter
Json.NET-Dokumentation
quelle
Für diejenigen, die neugierig auf den ConcreteListTypeConverter sind, auf den Oliver verwiesen hat, ist hier mein Versuch:
quelle
CanConvert(Type objectType) { return true;}
. Es scheint hacky, wie genau ist das hilfreich? Ich kann mich irren, aber ist das nicht so, als würde man einem kleineren unerfahrenen Kämpfer sagen, dass er den Kampf gewinnen wird, egal welcher Gegner?Für das, was es wert ist, musste ich mich größtenteils selbst darum kümmern. Jedes Objekt verfügt über eine Deserialize- Methode (Zeichenfolge jsonStream) . Ein paar Ausschnitte davon:
In diesem Fall ist new Thingy (Zeichenfolge) ein Konstruktor, der die Deserialize- Methode (Zeichenfolge jsonStream) des entsprechenden konkreten Typs aufruft . Dieses Schema wird weiter nach unten und unten verschoben, bis Sie zu den Basispunkten gelangen, die json.NET gerade verarbeiten kann.
Und so weiter und so fort. Mit diesem Setup konnte ich json.NET-Setups bereitstellen, die verarbeitet werden können, ohne dass ein großer Teil der Bibliothek selbst umgestaltet werden muss oder unhandliche Try / Parse-Modelle verwendet werden müssen, die aufgrund der Anzahl der beteiligten Objekte unsere gesamte Bibliothek blockiert hätten. Dies bedeutet auch, dass ich alle JSON-Änderungen an einem bestimmten Objekt effektiv verarbeiten kann und mich nicht um alles kümmern muss, was das Objekt berührt. Es ist keineswegs die ideale Lösung, aber es funktioniert recht gut mit unseren Unit- und Integrationstests.
quelle
Angenommen, eine Autofac-Einstellung wie die folgende:
Nehmen wir dann an, Ihre Klasse ist wie folgt:
Daher könnte die Verwendung des Resolvers bei der Deserialisierung wie folgt aussehen:
Weitere Informationen finden Sie unter http://www.newtonsoft.com/json/help/html/DeserializeWithDependencyInjection.htm
quelle
Kein Objekt wird immer sein , eine iThingy als Schnittstellen alle abstrakten per Definition.
Das Objekt, das Sie zuerst serialisiert haben, war von einem konkreten Typ und implementierte die abstrakte Schnittstelle. Sie müssen den gleichen Beton haben Klasse muss die serialisierten Daten wiederbeleben.
Das resultierende Objekt ist dann von einem Typ, der die von Ihnen gesuchte abstrakte Schnittstelle implementiert .
Aus der Dokumentation folgt, dass Sie verwenden können
beim Deserialisieren, um JSON.NET über den konkreten Typ zu informieren.
quelle
_type
Eigenschaft vorhanden ist, die den zu verwendenden Betontyp signalisiert.Meine Lösung für diese, die ich mag, weil sie sehr allgemein ist, lautet wie folgt:
}}
Sie können es offensichtlich und trivial in einen noch allgemeineren Konverter konvertieren, indem Sie einen Konstruktor hinzufügen, der ein Argument vom Typ Dictionary <Typ, Typ> verwendet, mit dem die Instanzvariable der Konvertierung instanziiert wird.
quelle
Einige Jahre später hatte ich ein ähnliches Problem. In meinem Fall gab es stark verschachtelte Schnittstellen und eine Präferenz für das Generieren der konkreten Klassen zur Laufzeit, damit es mit einer generischen Klasse funktioniert.
Ich habe beschlossen, zur Laufzeit eine Proxy-Klasse zu erstellen, die das von Newtonsoft zurückgegebene Objekt umschließt.
Der Vorteil dieses Ansatzes besteht darin, dass keine konkrete Implementierung der Klasse erforderlich ist und jede Tiefe verschachtelter Schnittstellen automatisch verarbeitet werden kann. Sie können mehr darüber in meinem Blog sehen .
Verwendung:
quelle
PopulateObject
auf dem von Impromptu Interface generierten Proxy zu machen. Ich habe es leider aufgegeben, mich für Duck Typing zu entscheiden - es war einfach einfacher, einen benutzerdefinierten Json Contract Serializer zu erstellen, der mithilfe von Reflection eine vorhandene Implementierung der angeforderten Schnittstelle fand und diese verwendete.Verwenden Sie diese JsonKnownTypes , es ist sehr ähnlich zu verwenden, es fügt nur einen Diskriminator zu json hinzu:
Wenn Sie nun ein Objekt in json serialisieren, wird es
"$type"
mit dem"myClass"
Wert addiert und zum Deserialisieren verwendetJson:
quelle
Meine Lösung wurde die Schnittstellenelemente im Konstruktor hinzugefügt.
quelle