Ich erhalte ein JSON-Objekt von einem AJAX-Aufruf an einen REST-Server. Dieses Objekt verfügt über Eigenschaftsnamen, die meiner TypeScript-Klasse entsprechen (dies ist eine Fortsetzung dieser Frage ).
Was ist der beste Weg, um es zu initialisieren? Ich denke nicht, dass dies funktionieren wird, da die Klasse (& JSON-Objekt) Mitglieder hat, die Listen von Objekten und Mitglieder sind, die Klassen sind, und diese Klassen Mitglieder haben, die Listen und / oder Klassen sind.
Aber ich würde einen Ansatz bevorzugen, bei dem die Mitgliedsnamen nachgeschlagen und zugewiesen werden, Listen erstellt und Klassen nach Bedarf instanziiert werden, sodass ich nicht für jedes Mitglied in jeder Klasse expliziten Code schreiben muss (es gibt eine Menge!).
json
typescript
David Thielen
quelle
quelle
Antworten:
Dies sind einige kurze Aufnahmen, um einige verschiedene Möglichkeiten aufzuzeigen. Sie sind keineswegs "vollständig" und als Haftungsausschluss halte ich es nicht für eine gute Idee, dies so zu tun. Außerdem ist der Code nicht zu sauber, da ich ihn nur ziemlich schnell zusammen geschrieben habe.
Auch als Hinweis: Natürlich müssen deserialisierbare Klassen Standardkonstruktoren haben, wie dies in allen anderen Sprachen der Fall ist, in denen mir Deserialisierung jeglicher Art bekannt ist. Natürlich wird sich Javascript nicht beschweren, wenn Sie einen nicht standardmäßigen Konstruktor ohne Argumente aufrufen, aber die Klasse sollte dann besser darauf vorbereitet sein (außerdem wäre es nicht wirklich der "Typenskript-Weg").
Option 1: Überhaupt keine Laufzeitinformationen
Das Problem bei diesem Ansatz besteht hauptsächlich darin, dass der Name eines Mitglieds mit seiner Klasse übereinstimmen muss. Dies beschränkt Sie automatisch auf ein Mitglied desselben Typs pro Klasse und verstößt gegen mehrere Regeln für bewährte Verfahren. Ich rate dringend davon ab, aber liste es einfach hier auf, weil es der erste "Entwurf" war, als ich diese Antwort schrieb (weshalb die Namen auch "Foo" usw. sind).
Option 2: Der Name Eigenschaft
Um das Problem in Option 1 zu beseitigen, benötigen wir Informationen darüber, welcher Typ ein Knoten im JSON-Objekt ist. Das Problem ist, dass diese Dinge in Typescript Konstrukte zur Kompilierungszeit sind und wir sie zur Laufzeit benötigen - aber Laufzeitobjekte kennen ihre Eigenschaften erst, wenn sie festgelegt sind.
Eine Möglichkeit besteht darin, die Klassen auf ihre Namen aufmerksam zu machen. Sie benötigen diese Eigenschaft jedoch auch im JSON. Eigentlich brauchst du es nur im json:
Option 3: Explizite Angabe der Elementtypen
Wie oben erwähnt, sind die Typinformationen von Klassenmitgliedern zur Laufzeit nicht verfügbar - es sei denn, wir stellen sie zur Verfügung. Wir müssen dies nur für nicht-primitive Mitglieder tun und es kann losgehen:
Option 4: Der ausführliche, aber ordentliche Weg
Update 01/03/2016: Wie @GameAlchemist in den Kommentaren betonte ( Idee , Implementierung ) ab Typescript 1.7 , kann die unten beschriebene Lösung mithilfe von Klassen- / Eigenschaftsdekoratoren besser geschrieben werden.
Serialisierung ist immer ein Problem und meiner Meinung nach ist der beste Weg ein Weg, der einfach nicht der kürzeste ist. Von allen Optionen würde ich dies bevorzugen, da der Autor der Klasse die volle Kontrolle über den Status deserialisierter Objekte hat. Wenn ich raten müsste, würde ich sagen, dass alle anderen Optionen früher oder später Sie in Schwierigkeiten bringen werden (es sei denn, Javascript bietet eine native Methode, um damit umzugehen).
Das folgende Beispiel wird der Flexibilität nicht gerecht. Es kopiert wirklich nur die Struktur der Klasse. Der Unterschied, den Sie hier beachten müssen, besteht darin, dass die Klasse die volle Kontrolle über die Verwendung jeder Art von JSON hat, die den Status der gesamten Klasse steuern soll (Sie können Dinge berechnen usw.).
quelle
equals
odertoString
Methoden (nur dass Sie diese normalerweise automatisch generieren lassen). Es sollte nicht zu schwierig sein, einen Generator zu schreiben,deserialize
wenn Sie möchten, aber es kann einfach keine Laufzeitautomatisierung sein.Sie können verwenden
Object.assign
Ich weiß nicht, wann dies hinzugefügt wurde, ich verwende derzeit Typescript 2.0.2, und dies scheint eine ES6-Funktion zu sein.hier ist
HalJson
Hier ist, was Chrom sagt, dass es ist
Sie können also sehen, dass die Zuweisung nicht rekursiv erfolgt
quelle
Object.assign
. Warum haben wir dann zwei lexikonartige Antworten über dieser?Object.assign
nicht rekursiv funktioniert und keine korrekten Objekttypen instanziiert werden, wobei Werte alsObject
Instanzen verbleiben . Während es für triviale Aufgaben in Ordnung ist, ist damit keine komplexe Serialisierung möglich. Wenn eine Klasseneigenschaft beispielsweise einen benutzerdefinierten Klassentyp hat, instanziiertJSON.parse
+Object.assign
diese Eigenschaft aufObject
. Zu den Nebenwirkungen zählen fehlende Methoden und Zugriffsmethoden.Object.assign
, wenn es immer noch darauf ankommt, verschachtelte Instanziierungen von zu schreiben Hand. Dieser Ansatz eignet sich für sehr einfache Objekte auf Tutorial-Ebene, jedoch nicht für den tatsächlichen Gebrauch.TLDR: TypedJSON (funktionierender Proof of Concept)
Die Wurzel der Komplexität dieses Problems liegt darin, dass JSON zur Laufzeit mithilfe von Typinformationen deserialisiert werden muss , die nur zur Kompilierungszeit vorhanden sind . Dies erfordert, dass Typinformationen zur Laufzeit irgendwie verfügbar gemacht werden.
Glücklicherweise kann dies mit Dekorateuren und ReflectDecorators auf sehr elegante und robuste Weise gelöst werden :
Typinformationen aufzeichnen
Mit einer Kombination aus ReflectDecorators und Eigenschaftsdekoratoren können Typinformationen zu einer Eigenschaft einfach aufgezeichnet werden. Eine rudimentäre Umsetzung dieses Ansatzes wäre:
Für eine bestimmte Eigenschaft fügt das obige Snippet der versteckten
__propertyTypes__
Eigenschaft des Klassenprototyps einen Verweis auf die Konstruktorfunktion der Eigenschaft hinzu . Beispielsweise:Und das war's, wir haben zur Laufzeit die erforderlichen Typinformationen, die jetzt verarbeitet werden können.
Typinformationen verarbeiten
Wir müssen zuerst eine
Object
Instanz mit erhaltenJSON.parse
- danach können wir über die gesamten Einträge in__propertyTypes__
(oben gesammelt) iterieren und die erforderlichen Eigenschaften entsprechend instanziieren. Der Typ des Stammobjekts muss angegeben werden, damit der Deserializer einen Ausgangspunkt hat.Wiederum wäre eine absolut einfache Implementierung dieses Ansatzes:
Die obige Idee hat den großen Vorteil, dass sie nach erwarteten Typen (für komplexe / Objektwerte) deserialisiert wird , anstatt nach dem, was im JSON vorhanden ist. Wenn a
Person
erwartet wird, wird einePerson
Instanz erstellt. Mit einigen zusätzlichen Sicherheitsmaßnahmen für primitive Typen und Arrays kann dieser Ansatz sicher gemacht werden, der jedem böswilligen JSON widersteht .Randfälle
Wenn Sie jetzt aber freuen uns , dass die Lösung ist , dass einfach, habe er eine schlechte Nachricht: Es ist eine große Anzahl von Grenzfällen , die betreut werden müssen. Nur einige davon sind:
Wenn Sie nicht mit all diesen Dingen herumspielen möchten (ich wette, Sie tun es nicht), würde ich Ihnen gerne eine funktionierende experimentelle Version eines Proof-of-Concept empfehlen, der diesen Ansatz verwendet, TypedJSON - den ich erstellt habe Um genau dieses Problem anzugehen, stelle ich mich täglich.
Aufgrund der Tatsache, dass Dekorateure immer noch als experimentell angesehen werden, würde ich nicht empfehlen, es für Produktionszwecke zu verwenden, aber bisher hat es mir gute Dienste geleistet.
quelle
Ich habe diesen Typen benutzt, um die Arbeit zu erledigen: https://github.com/weichx/cerialize
Es ist sehr einfach und doch mächtig. Es unterstützt:
Beispiel:
quelle
Ich habe ein Tool erstellt, das TypeScript-Schnittstellen und eine Laufzeit- "Typzuordnung" generiert, um eine Laufzeit-Typprüfung anhand der Ergebnisse von
JSON.parse
: ts.quicktype.io durchzuführenBeispiel: JSON:
quicktype erzeugt die folgende TypeScript-Oberfläche und Type Map:
Dann überprüfen wir das Ergebnis von anhand
JSON.parse
der Typenkarte:Ich habe einen Code weggelassen , aber Sie können versuchen, die Details mit Quicktype zu überprüfen .
quelle
Option 5: Verwenden von Typescript-Konstruktoren und jQuery.extend
Dies scheint die am besten zu wartende Methode zu sein: Fügen Sie einen Konstruktor hinzu, der die JSON-Struktur als Parameter verwendet, und erweitern Sie das JSON-Objekt. Auf diese Weise können Sie eine JSON-Struktur in das gesamte Anwendungsmodell analysieren.
Es ist nicht erforderlich, Schnittstellen zu erstellen oder Eigenschaften im Konstruktor aufzulisten.
In Ihrem Ajax-Rückruf, in dem Sie ein Unternehmen zur Berechnung der Gehälter erhalten:
quelle
$.extend
kommenObject.assign
, wodurch diese Abhängigkeit von jQuery beseitigt wird.Für einfache Objekte mag ich diese Methode:
Durch die Nutzung der Fähigkeit, Eigenschaften im Konstruktor zu definieren, kann dieser präzise gestaltet werden.
Dadurch erhalten Sie ein typisiertes Objekt (im Vergleich zu allen Antworten, die Object.assign oder eine Variante verwenden, die Ihnen ein Objekt gibt) und benötigen keine externen Bibliotheken oder Dekoratoren.
quelle
Die oben beschriebene 4. Option ist eine einfache und nette Möglichkeit, die mit der 2. Option kombiniert werden muss, wenn Sie eine Klassenhierarchie wie beispielsweise eine Mitgliederliste behandeln müssen, bei der es sich um Vorkommen von Unterklassen von handelt Ein Mitglied der Superklasse, z. B. Direktor erweitert Mitglied oder Schüler erweitert Mitglied. In diesem Fall müssen Sie den Unterklassentyp im JSON-Format angeben
quelle
JQuery .extend erledigt dies für Sie:
quelle
Vielleicht keine tatsächliche, aber einfache Lösung:
arbeite auch für schwierige Abhängigkeiten !!!
quelle
baz
sind sie vom TypObject
und nicht vom Typ.Bar.
Dies funktioniert in diesem einfachen Fall, daBar
keine Methoden (nur primitive Eigenschaften) vorhanden sind. WennBar
eine Methode wieisEnabled()
diese vorhanden wäre, würde dieser Ansatz fehlschlagen, da diese Methode nicht in der serialisierten JSON-Zeichenfolge enthalten wäre.Eine weitere Option mit Fabriken
https://github.com/MrAntix/ts-deserialize
benutze so
quelle
Das Beste, was ich für diesen Zweck gefunden habe, ist der Klassentransformator. github.com/typestack/class-transformer
So benutzt du es:
Einige Klasse:
Wenn Sie den @ Type Decorator verwenden, werden auch verschachtelte Eigenschaften erstellt.
quelle
Ich persönlich bevorzuge Option 3 von @Ingo Bürk. Und ich habe seine Codes verbessert, um ein Array komplexer Daten und ein Array primitiver Daten zu unterstützen.
quelle
Sie können wie unten tun
und
quelle
quelle