Je reçois un objet JSON d'un appel AJAX vers un serveur REST. Cet objet a des noms de propriété qui correspondent à ma classe TypeScript (c'est une suite à cette question ).
Quelle est la meilleure façon de l'initialiser? Je ne pense pas que cela fonctionnera parce que la classe (objet & JSON) a des membres qui sont des listes d'objets et des membres qui sont des classes, et ces classes ont des membres qui sont des listes et / ou des classes.
Mais je préférerais une approche qui recherche les noms des membres et les attribue à travers, créant des listes et instanciant les classes selon les besoins, donc je n'ai pas à écrire de code explicite pour chaque membre de chaque classe (il y a beaucoup!)
json
typescript
David Thielen
la source
la source
Réponses:
Voici quelques plans rapides pour montrer quelques façons différentes. Ils ne sont en aucun cas "complets" et en tant que déni de responsabilité, je ne pense pas que ce soit une bonne idée de le faire comme ça. De plus, le code n'est pas trop propre car je viens de le taper assez rapidement.
Également comme note: Bien sûr, les classes désérialisables doivent avoir des constructeurs par défaut comme c'est le cas dans tous les autres langages où je suis au courant de la désérialisation de toute sorte. Bien sûr, Javascript ne se plaindra pas si vous appelez un constructeur non par défaut sans arguments, mais la classe devrait alors y être préparée (en plus, ce ne serait pas vraiment la "méthode de la typographie").
Option n ° 1: aucune information d'exécution
Le problème avec cette approche est principalement que le nom d'un membre doit correspondre à sa classe. Ce qui vous limite automatiquement à un membre du même type par classe et enfreint plusieurs règles de bonne pratique. Je déconseille fortement cela, mais inscrivez-le simplement ici parce que c'était le premier "brouillon" quand j'ai écrit cette réponse (c'est aussi pourquoi les noms sont "Foo" etc.).
Option # 2: la propriété name
Pour se débarrasser du problème dans l'option # 1, nous devons avoir une sorte d'informations sur le type d'un nœud dans l'objet JSON. Le problème est que dans Typescript, ces choses sont des constructions au moment de la compilation et nous en avons besoin lors de l'exécution - mais les objets d'exécution n'ont tout simplement pas conscience de leurs propriétés jusqu'à ce qu'ils soient définis.
Une façon de le faire consiste à informer les classes de leurs noms. Vous avez également besoin de cette propriété dans le JSON. En fait, vous n'en avez besoin que dans le json:
Option n ° 3: indiquer explicitement les types de membres
Comme indiqué ci-dessus, les informations de type des membres de la classe ne sont pas disponibles au moment de l'exécution, c'est-à-dire à moins que nous ne les rendions disponibles. Nous n'avons besoin de faire cela que pour les membres non primitifs et nous sommes prêts à partir:
Option # 4: La manière verbeuse, mais soignée
Mise à jour du 01/03/2016: Comme @GameAlchemist l'a souligné dans les commentaires ( idée , implémentation ), à partir de Typescript 1.7, la solution décrite ci-dessous peut être mieux écrite à l'aide de décorateurs de classe / propriété.
La sérialisation est toujours un problème et à mon avis, le meilleur moyen est un moyen qui n'est tout simplement pas le plus court. De toutes les options, c'est ce que je préférerais parce que l'auteur de la classe a un contrôle total sur l'état des objets désérialisés. Si je devais deviner, je dirais que toutes les autres options, tôt ou tard, vous causeront des ennuis (à moins que Javascript ne propose une méthode native pour y faire face).
Vraiment, l'exemple suivant ne rend pas justice à la flexibilité. Il ne fait que copier la structure de la classe. La différence que vous devez garder à l'esprit ici, cependant, est que la classe a le contrôle total pour utiliser tout type de JSON qu'elle souhaite contrôler l'état de la classe entière (vous pouvez calculer des choses, etc.).
la source
equals
ou auxtoString
méthodes (seulement que vous les avez généralement générées automatiquement). Il ne devrait pas être trop difficile d'écrire un générateurdeserialize
si vous le souhaitez, mais il ne peut tout simplement pas s'agir d'une automatisation d'exécution.vous pouvez utiliser
Object.assign
Je ne sais pas quand cela a été ajouté, j'utilise actuellement Typescript 2.0.2, et cela semble être une fonctionnalité ES6.voici
HalJson
voici ce que dit chrome
de sorte que vous pouvez voir qu'il ne fait pas l'attribution récursivement
la source
Object.assign
. Pourquoi avons-nous alors deux réponses de type lexique au-dessus de celle-ci?Object.assign
que ne fonctionnera pas récursivement et n'instanciera pas les types d'objets corrects, en laissant les valeurs commeObject
instances. Bien qu'il convienne aux tâches triviales, la sérialisation de type complexe n'est pas possible avec. Par exemple, si une propriété de classe est d'un type de classe personnalisé,JSON.parse
+Object.assign
instanciera cette propriété enObject
. Les effets secondaires incluent les méthodes et les accessoires manquants.Object.assign
, où il s'agit toujours d'écrire une instanciation imbriquée par main. Cette approche convient parfaitement aux objets très simples de niveau didacticiel, mais pas à une utilisation réelle.TLDR: TypedJSON (preuve de concept de travail)
La racine de la complexité de ce problème est que nous devons désérialiser JSON à l' exécution en utilisant des informations de type qui n'existent qu'au moment de la compilation . Cela nécessite que les informations de type soient en quelque sorte mises à disposition au moment de l'exécution.
Heureusement, cela peut être résolu de manière très élégante et robuste avec les décorateurs et ReflectDecorators :
Enregistrement des informations de type
Avec une combinaison de ReflectDecorators et de décorateurs de propriétés, les informations de type peuvent être facilement enregistrées sur une propriété. Une mise en œuvre rudimentaire de cette approche serait:
Pour toute propriété donnée, l'extrait ci-dessus ajoutera une référence de la fonction constructeur de la propriété à la
__propertyTypes__
propriété cachée du prototype de classe. Par exemple:Et c'est tout, nous avons les informations de type requises lors de l'exécution, qui peuvent maintenant être traitées.
Traitement des informations de type
Nous devons d'abord obtenir une
Object
instance en utilisantJSON.parse
- après cela, nous pouvons parcourir les entrées__propertyTypes__
(collectées ci-dessus) et instancier les propriétés requises en conséquence. Le type de l'objet racine doit être spécifié pour que le désérialiseur ait un point de départ.Encore une fois, une simple mise en œuvre de cette approche serait:
L'idée ci-dessus présente un grand avantage de désérialisation par les types attendus (pour les valeurs complexes / objet), au lieu de ce qui est présent dans le JSON. Si un
Person
est attendu, c'est unePerson
instance qui est créée. Avec quelques mesures de sécurité supplémentaires en place pour les types et les tableaux primitifs, cette approche peut être sécurisée, qui résiste à tout JSON malveillant.Étuis Edge
Cependant, si vous êtes maintenant heureux que la solution soit aussi simple, j'ai une mauvaise nouvelle: il y a un grand nombre de cas marginaux qui doivent être pris en charge. Seuls certains d'entre eux sont:
Si vous ne voulez pas jouer avec tout cela (je parie que vous ne le faites pas), je serais heureux de recommander une version expérimentale de travail d'une preuve de concept utilisant cette approche, TypedJSON - que j'ai créé pour s'attaquer à ce problème précis, un problème auquel je suis confronté quotidiennement.
En raison de la façon dont les décorateurs sont toujours considérés comme expérimentaux, je ne recommanderais pas de l'utiliser pour la production, mais jusqu'à présent, cela m'a bien servi.
la source
J'utilise ce type pour faire le travail: https://github.com/weichx/cerialize
C'est très simple mais puissant. Elle supporte:
Exemple:
la source
J'ai créé un outil qui génère des interfaces TypeScript et une "mappe de types" d'exécution pour effectuer une vérification typographique d'exécution par rapport aux résultats de
JSON.parse
: ts.quicktype.ioPar exemple, étant donné ce JSON:
quicktype produit l'interface TypeScript et la carte de types suivantes:
Ensuite, nous vérifions le résultat de
JSON.parse
la carte de type:J'ai omis du code, mais vous pouvez essayer de saisir rapidement les détails.
la source
Option n ° 5: utilisation des constructeurs Typescript et jQuery.extend
Cela semble être la méthode la plus maintenable: ajoutez un constructeur qui prend en paramètre la structure json et étendez l'objet json. De cette façon, vous pouvez analyser une structure json dans l'ensemble du modèle d'application.
Il n'est pas nécessaire de créer des interfaces ou de répertorier les propriétés dans le constructeur.
Dans votre rappel ajax où vous recevez une entreprise pour calculer les salaires:
la source
$.extend
viens-tu?Object.assign
, ce qui supprime cette dépendance à jQuery.Pour les objets simples, j'aime cette méthode:
Tirer parti de la capacité de définir des propriétés dans le constructeur permet d'être concis.
Cela vous donne un objet typé (par rapport à toutes les réponses qui utilisent Object.assign ou une variante, qui vous donne un objet) et ne nécessite pas de bibliothèques ou de décorateurs externes.
la source
La 4ème option décrite ci-dessus est une façon simple et agréable de le faire, qui doit être combinée avec la 2ème option dans le cas où vous devez gérer une hiérarchie de classes comme par exemple une liste de membres qui est l'une des occurrences de sous-classes de une super classe de membre, par exemple le directeur étend le membre ou l'étudiant étend le membre. Dans ce cas, vous devez donner le type de sous-classe au format json
la source
JQuery .extend fait ceci pour vous:
la source
le meilleur que j'ai trouvé à cet effet est le transformateur de classe. github.com/typestack/class-transformer
Voilà comment vous l'utilisez:
Certains cours:
Si vous utilisez le décorateur @Type, les propriétés imbriquées seront également créées.
la source
Peut-être pas une solution réelle, mais simple:
fonctionne aussi pour les dépendances difficiles !!!
la source
baz
ils seront de typeObject
et non de type.Bar.
Cela fonctionne dans ce cas simple carBar
n'a pas de méthodes (juste des propriétés primitives). SiBar
une méthode similaire existaitisEnabled()
, cette approche échouerait car cette méthode ne serait pas dans la chaîne JSON sérialisée.Une autre option en utilisant des usines
https://github.com/MrAntix/ts-deserialize
utiliser comme ça
la source
Personnellement, je préfère l'option n ° 3 de @Ingo Bürk. Et j'ai amélioré ses codes pour prendre en charge un tableau de données complexes et un tableau de données primitives.
la source
vous pouvez faire comme ci-dessous
et
la source
la source