Comment convertir un objet JSON en une classe dactylographiée

394

J'ai lu un objet JSON à partir d'un serveur REST distant. Cet objet JSON a toutes les propriétés d'une classe dactylographiée (par conception). Comment convertir cet objet JSON reçu en un type var?

Je ne veux pas remplir une var dactylographiée (c'est-à-dire avoir un constructeur qui prend cet objet JSON). Il est grand et tout copier sous-objet par sous-objet et propriété par propriété prendrait beaucoup de temps.

Mise à jour: Vous pouvez cependant le diffuser sur une interface dactylographiée!

David Thielen
la source
vous pouvez utiliser github.com/vojtechhabarta/typescript-generator pour générer des interfaces TypeScript au cas où votre JSON est mappé à l'aide de classes Java
Vojta
J'ai codé une petite bibliothèque de casting: sulphur-blog.azurewebsites.net/typescript-mini-cast-library
Camille Wintz
1
Objet JSON = objet JavaScript Notation d'objet. Il n'y a pas assez d'objets là-dedans, je dis que nous en ajoutons quelques autres pour faire bonne mesure.
bug-a-lot
1
J'ai créé un outil pour ce beshanoe.github.io/json2ts
beshanoe
La création d'un prototype de classe TypeScript pour définir votre objet ne nuira pas au vrai code de production. Jetez un œil au fichier JS compilé, toutes les définitions seront supprimées, car elles ne font pas partie de JS.
FisNaN

Réponses:

167

Vous ne pouvez pas simplement convertir un résultat JavaScript purement ancien d'une demande Ajax en une instance de classe JavaScript / TypeScript prototypique. Il existe un certain nombre de techniques pour le faire et impliquent généralement la copie de données. À moins que vous ne créiez une instance de la classe, elle n'aura aucune méthode ou propriété. Il restera un simple objet JavaScript.

Alors que si vous ne traitiez que des données, vous pouvez simplement effectuer un cast vers une interface (car il s'agit purement d'une structure de temps de compilation), cela nécessiterait que vous utilisiez une classe TypeScript qui utilise l'instance de données et effectue des opérations avec ces données.

Quelques exemples de copie des données:

  1. Copie d'un objet AJAX JSON dans un objet existant
  2. Analyser la chaîne JSON dans un prototype d'objet particulier en JavaScript

En substance, vous voudriez juste:

var d = new MyRichObject();
d.copyInto(jsonResult);
WiredPrairie
la source
Je suis d'accord avec ta réponse. En outre, bien que je ne sois pas en mesure de le rechercher et de le tester en ce moment, je pense que ces deux étapes pourraient être combinées en donnant une fonction de réveil comme paramètre JSON.parse(). Les deux devraient encore être effectués, mais syntaxiquement, ils pourraient être combinés.
JAAulde
Bien sûr, cela pourrait aussi fonctionner - je ne sais pas si ce serait plus efficace, car il faudrait appeler un appel de fonction supplémentaire pour chaque propriété.
WiredPrairie
Certainement pas la réponse que je cherchais :( Par curiosité pourquoi est-ce? Il me semble que le fonctionnement de javascript devrait être faisable.
David Thielen
Non, cela ne fonctionne pas en TypeScript car il n'y a pas de moyen simple en JavaScript pour le faire.
WiredPrairie
1
Qu'en est-ilObject.setPrototypeOf
Petah
103

J'ai eu le même problème et j'ai trouvé une bibliothèque qui fait le travail: https://github.com/pleerock/class-transformer .

Cela fonctionne comme ceci:

let jsonObject = response.json() as Object;
let fooInstance = plainToClass(Models.Foo, jsonObject);
return fooInstance;

Il prend en charge les enfants imbriqués, mais vous devez décorer le membre de votre classe.

Pak
la source
14
Cette brillante petite bibliothèque l'a parfaitement résolu avec le moins d'effort (n'oubliez pas vos @Typeannotations, cependant). Cette réponse mérite plus de crédit.
Benny Bottema
Oh wow !, cette bibliothèque n'est pas si petite qu'elle a peut-être tout ce dont vous avez besoin, vous permet même de contrôler la transformation avec le décorateur @transform: D
Diego Fernando Murillo Valenci
3
Notez que cette bibliothèque est à peine maintenue. Cela ne fonctionne plus avec Angular5 + et comme ils ne fusionnent même plus les demandes de tirage, je ne pense pas qu'ils y travailleront de sitôt. C'est une grande bibliothèque cependant.
kentor
Il existe une solution de contournement pour Angular5 + (il s'agit en fait d'un bogue angulaire): github.com/typestack/class-transformer/issues/108
Pak
2
Cela fonctionne très bien dans Angular 6 (au moins pour mon cas d'utilisation qui consiste simplement à mapper littéralement la classe JSON <=>)
tftd
54

Dans TypeScript, vous pouvez faire une assertion de type en utilisant une interface et des génériques comme ceci:

var json = Utilities.JSONLoader.loadFromFile("../docs/location_map.json");
var locations: Array<ILocationMap> = JSON.parse(json).location;

Où ILocationMap décrit la forme de vos données. L'avantage de cette méthode est que votre JSON peut contenir plus de propriétés mais la forme satisfait les conditions de l'interface.

J'espère que ça aide!

user756310
la source
49
FYI: C'est une assertion de type, pas un casting.
WiredPrairie
6
Voir ici pour la différence entre une assertion de type et un cast .
Stefan Hanke
7
Où puis-je trouver Utilities.JSONLoader?
HypeXR
22
Mais il n'aura aucune méthode, comme mentionné dans la réponse.
Martín Coll
1
@StefanHanke semble que l'URL a légèrement changé: "Type Assertion vs. Casting"
ruffin
37

Si vous utilisez ES6, essayez ceci:

class Client{
  name: string

  displayName(){
    console.log(this.name)
  }
}

service.getClientFromAPI().then(clientData => {

  // Here the client data from API only have the "name" field
  // If we want to use the Client class methods on this data object we need to:
  let clientWithType = Object.assign(new Client(), clientData)

  clientWithType.displayName()
})

Malheureusement, cette méthode ne fonctionnera pas sur l'objet nid .

migcoder
la source
4
Ils l'ont demandé dans Typescript.
joe.feser
HI @ joe.feser, je mentionne ES6 car de cette façon la méthode 'Object.assign' est requise.
migcoder
1
Dans le cas où le constructeur par défaut est manquant, l'instance cible peut être créée via Object.create(MyClass.prototype), en contournant complètement le constructeur.
Marcello
Plus d'explications sur la limitation des objets imbriqués voir dans stackoverflow.com/questions/22885995/…
Michael Freidgeim
28

J'ai trouvé un article très intéressant sur la conversion générique de JSON en classe Typescript:

http://cloudmark.github.io/Json-Mapping/

Vous vous retrouvez avec le code suivant:

let example = {
                "name": "Mark", 
                "surname": "Galea", 
                "age": 30, 
                "address": {
                  "first-line": "Some where", 
                  "second-line": "Over Here",
                  "city": "In This City"
                }
              };

MapUtils.deserialize(Person, example);  // custom class
Philippe
la source
20

TLDR: une doublure

// This assumes your constructor method will assign properties from the arg.
.map((instanceData: MyClass) => new MyClass(instanceData));

La réponse détaillée

Je ne recommanderais pas l'approche Object.assign, car elle peut joncher de manière inappropriée votre instance de classe avec des propriétés non pertinentes (ainsi que des fermetures définies) qui n'ont pas été déclarées dans la classe elle-même.

Dans la classe dans laquelle vous essayez de désérialiser, je m'assurerais que toutes les propriétés que vous souhaitez désérialiser soient définies (null, tableau vide, etc.). En définissant vos propriétés avec des valeurs initiales, vous exposez leur visibilité lorsque vous essayez d'itérer des membres de classe auxquels affecter des valeurs (voir la méthode de désérialisation ci-dessous).

export class Person {
  public name: string = null;
  public favoriteSites: string[] = [];

  private age: number = null;
  private id: number = null;
  private active: boolean;

  constructor(instanceData?: Person) {
    if (instanceData) {
      this.deserialize(instanceData);
    }
  }

  private deserialize(instanceData: Person) {
    // Note this.active will not be listed in keys since it's declared, but not defined
    const keys = Object.keys(this);

    for (const key of keys) {
      if (instanceData.hasOwnProperty(key)) {
        this[key] = instanceData[key];
      }
    }
  }
}

Dans l'exemple ci-dessus, j'ai simplement créé une méthode de désérialisation. Dans un exemple réel, je l'aurais centralisé dans une classe de base réutilisable ou une méthode de service.

Voici comment l'utiliser dans quelque chose comme un http resp ...

this.http.get(ENDPOINT_URL)
  .map(res => res.json())
  .map((resp: Person) => new Person(resp) ) );

Si tslint / ide se plaint que le type d'argument est incompatible, il suffit de transposer l'argument dans le même type à l'aide de crochets angulaires <YourClassName>, exemple:

const person = new Person(<Person> { name: 'John', age: 35, id: 1 });

Si vous avez des membres de classe qui sont d'un type spécifique (aka: instance d'une autre classe), vous pouvez les faire transtyper en instances typées via des méthodes getter / setter.

export class Person {
  private _acct: UserAcct = null;
  private _tasks: Task[] = [];

  // ctor & deserialize methods...

  public get acct(): UserAcct {
    return this.acct;
  }
  public set acct(acctData: UserAcct) {
    this._acct = new UserAcct(acctData);
  }

  public get tasks(): Task[] {
    return this._tasks;
  }

  public set tasks(taskData: Task[]) {
    this._tasks = taskData.map(task => new Task(task));
  }
}

L'exemple ci-dessus désérialise à la fois acct et la liste des tâches dans leurs instances de classe respectives.

Timothy Perez
la source
Je reçois ce message d'erreur: Le type '{nom: chaîne, âge: numéro, id: numéro}' ne peut pas être converti en type 'Personne'. La propriété 'id' est privée dans le type 'Person' mais pas dans le type '{name: string, age: number, id: number}'
utiq
Comment dois-je l'utiliser avec des énumérations? Dois-je utiliser l'approche de type spécifique et y ajouter getter et setter?
Tadija Bagarić
@TimothyParez Quand définissez-vous les tâches?
Kay
J'ai essayé de faire quelque chose de similaire, mais mon tableau de tâches est vide lorsque je console.log personne.
Kay
Pour cela, j'ai dû ajouter une signature d'index à la classe: export class Person {[key: string]: any (...)}
Asimov
18

En supposant que json a les mêmes propriétés que votre classe dactylographiée, vous n'avez pas à copier vos propriétés Json dans votre objet dactylographié. Il vous suffira de construire votre objet Typescript en passant les données json dans le constructeur.

Dans votre rappel ajax, vous recevez une entreprise:

onReceiveCompany( jsonCompany : any ) 
{
   let newCompany = new Company( jsonCompany );

   // call the methods on your newCompany object ...
}

Pour que cela fonctionne:

1) Ajoutez un constructeur dans votre classe Typescript qui prend les données json comme paramètre. Dans ce constructeur vous étendez votre objet JSON avec jQuery, comme ceci: $.extend( this, jsonData). $ .extend permet de conserver les prototypes javascript tout en ajoutant les propriétés de l'objet json.

2) Notez que vous devrez faire de même pour les objets liés. Dans le cas des employés dans l'exemple, vous créez également un constructeur prenant la partie des données json pour les employés. Vous appelez $ .map pour traduire les employés json en objets employés dactylographiés.

export class Company
{
    Employees : Employee[];

    constructor( jsonData: any )
    {
        $.extend( this, jsonData);

        if ( jsonData.Employees )
            this.Employees = $.map( jsonData.Employees , (emp) => {
                return new Employee ( emp );  });
    }
}

export class Employee
{
    name: string;
    salary: number;

    constructor( jsonData: any )
    {
        $.extend( this, jsonData);
    }
}

C'est la meilleure solution que j'ai trouvée en traitant des classes Typescript et des objets json.

Anthony Brenelière
la source
Je préfère cette solution à l'implémentation et à la maintenance des interfaces, car mes applications Angular2 ont un vrai modèle d'application qui peut être différent du modèle des services web que consomme mon application. Il peut avoir des données privées et des propriétés calculées.
Anthony Brenelière
7
L'utilisation de JQuery dans des projets angulaires est une idée terrible. Et si vos modèles contiennent un tas de fonctions, ils ne sont plus des modèles.
Davor
1
@Davor Vous voulez dire POJO, ou modèle? POJO (essentiellement des objets simples) n'ont pas de fonctions, tandis que le modèle est un terme plus large, et il comprend un référentiel. Le modèle de référentiel, contrairement à POJO, concerne les fonctions, mais c'est toujours un modèle.
forsberg
@Davor: utiliser JQuery dans des projets angulaires n'est pas une mauvaise idée tant que vous ne l'utilisez pas pour manipuler le DOM, ce qui est en effet une idée terrible. J'utilise n'importe quelle bibliothèque dont j'ai besoin pour mes projets Angular, et pour jQuery ce n'est pas une option car mon projet utilise SignalR qui en dépend. Dans le cas des classes, désormais utilisées par javascript ES6, les données sont accessibles avec des propriétés qui sont des fonctions qui encapsulent la façon dont les données sont stockées en mémoire. Pour les constructeurs, il existe une bonne façon d'utiliser les usines.
Anthony Brenelière
L'OP porte clairement sur des modèles de données simples pour REST. Vous compliquez inutilement les choses, les gars. Et oui, vous pouvez utiliser Jquery pour des choses supplémentaires, mais vous importez une bibliothèque massive pour en utiliser 1%. C'est une odeur de code si j'en ai déjà vu une.
Davor
18

Il n'y a encore rien à vérifier automatiquement si l'objet JSON que vous avez reçu du serveur a les propriétés d'interface attendues (la lecture est conforme aux) typescript. Mais vous pouvez utiliser des gardes de type définis par l'utilisateur

Considérant l'interface suivante et un objet json idiot (il aurait pu être de n'importe quel type):

interface MyInterface {
    key: string;
 }

const json: object = { "key": "value" }

Trois voies possibles:

A. Type Assertion ou distribution statique simple placée après la variable

const myObject: MyInterface = json as MyInterface;

B. Fonte statique simple, avant la variable et entre les diamants

const myObject: MyInterface = <MyInterface>json;

C. Cast dynamique avancé, vous vérifiez vous-même la structure de l'objet

function isMyInterface(json: any): json is MyInterface {
    // silly condition to consider json as conform for MyInterface
    return typeof json.key === "string";
}

if (isMyInterface(json)) {
    console.log(json.key)
}
else {
        throw new Error(`Expected MyInterface, got '${json}'.`);
}

Vous pouvez jouer avec cet exemple ici

Notez que la difficulté ici est d'écrire la isMyInterfacefonction. J'espère que TS ajoutera tôt ou tard un décorateur pour exporter la saisie complexe vers le runtime et laisser le runtime vérifier la structure de l'objet en cas de besoin. Pour l'instant, vous pouvez soit utiliser un validateur de schéma json dont le but est approximativement le même OU ce générateur de fonction de vérification du type d'exécution

Flavien Volken
la source
1
Votre réponse devrait être au top je pense
afzalex
Absolument. C'est la meilleure réponse qui repose directement sur le site Web de ts.
Burak Karakuş
15

Dans mon cas, cela fonctionne. J'ai utilisé les fonctions Object.assign (cible, sources ...) . Tout d'abord, la création de l'objet correct, puis copie les données de l'objet json vers la cible.

let u:User = new User();
Object.assign(u , jsonUsers);

Et un exemple d'utilisation plus avancé. Un exemple utilisant le tableau.

this.someService.getUsers().then((users: User[]) => {
  this.users = [];
  for (let i in users) {
    let u:User = new User();
    Object.assign(u , users[i]);
    this.users[i] = u;
    console.log("user:" + this.users[i].id);
    console.log("user id from function(test it work) :" + this.users[i].getId());
  }

});

export class User {
  id:number;
  name:string;
  fullname:string;
  email:string;

  public getId(){
    return this.id;
  }
}
Adam111p
la source
Que se passe-t-il lorsque vous avez une propriété privée?
prasanthv
Parce que l'objet jsonUser n'est pas une classe utilisateur. Sans opération Object.assign (u, jsonUsers); Vous ne pouvez pas utiliser la fonction getId (). Ce n'est qu'après assign que vous obtenez un objet utilisateur valide dans lequel vous pouvez utiliser la fonction getId (). La fonction getId () est uniquement pour l'exemple que l'opération a réussi.
Adam111p
vous pouvez ignorer la var temp - juste fairethis.users[i] = new User(); Object.assign(this.users[i], users[i])
cyptus
ou encore mieux utiliser la valeur de retour:this.users[i] = Object.assign(new User(), users[i]);
cyptus
Cette version longue est à titre explicatif uniquement. Vous pouvez raccourcir le code autant que vous le souhaitez :)
Adam111p
6

Bien qu'il ne soit pas un casting en soi; J'ai trouvé que https://github.com/JohnWhiteTB/TypedJSON était une alternative utile.

@JsonObject
class Person {
    @JsonMember
    firstName: string;

    @JsonMember
    lastName: string;

    public getFullname() {
        return this.firstName + " " + this.lastName;
    }
}
var person = TypedJSON.parse('{ "firstName": "John", "lastName": "Doe" }', Person);

person instanceof Person; // true
person.getFullname(); // "John Doe"
Neil
la source
1
Il ne fait pas de casting, que fait-il vraiment?
DanielM
1
Cette solution nécessite énormément d'annotations. N'y a-t-il vraiment aucun moyen plus simple?
JPNotADragon
3

Vous pouvez créer un interfacede votre type ( SomeType) et y insérer l'objet.

const typedObject: SomeType = <SomeType> responseObject;
Jayant Varshney
la source
3

Si vous devez convertir votre objet json en une classe dactylographiée et avoir ses méthodes d'instance disponibles dans l'objet résultant que vous devez utiliser Object.setPrototypeOf, comme je l'ai fait dans l'extrait de code ci-dessous:

Object.setPrototypeOf(jsonObject, YourTypescriptClass.prototype)
s_bighead
la source
2

Une vieille question avec des réponses généralement correctes mais pas très efficaces. Voici ce que je propose:

Créez une classe de base qui contient la méthode init () et les méthodes de transtypage statiques (pour un seul objet et un tableau). Les méthodes statiques peuvent être n'importe où; la version avec la classe de base et init () permet ensuite des extensions faciles.

export class ContentItem {
    // parameters: doc - plain JS object, proto - class we want to cast to (subclass of ContentItem)
    static castAs<T extends ContentItem>(doc: T, proto: typeof ContentItem): T {
        // if we already have the correct class skip the cast
        if (doc instanceof proto) { return doc; }
        // create a new object (create), and copy over all properties (assign)
        const d: T = Object.create(proto.prototype);
        Object.assign(d, doc);
        // reason to extend the base class - we want to be able to call init() after cast
        d.init(); 
        return d;
    }
    // another method casts an array
    static castAllAs<T extends ContentItem>(docs: T[], proto: typeof ContentItem): T[] {
        return docs.map(d => ContentItem.castAs(d, proto));
    }
    init() { }
}

Des mécanismes similaires (avec assign () ) ont été mentionnés dans @ Adam111p post. Juste une autre façon (plus complète) de le faire. @Timothy Perez critique critique assign () , mais à mon humble avis, il est tout à fait approprié ici.

Implémentez une classe dérivée (la vraie):

import { ContentItem } from './content-item';

export class SubjectArea extends ContentItem {
    id: number;
    title: string;
    areas: SubjectArea[]; // contains embedded objects
    depth: number;

    // method will be unavailable unless we use cast
    lead(): string {
        return '. '.repeat(this.depth);
    }

    // in case we have embedded objects, call cast on them here
    init() {
        if (this.areas) {
            this.areas = ContentItem.castAllAs(this.areas, SubjectArea);
        }
    }
}

Maintenant, nous pouvons lancer un objet récupéré du service:

const area = ContentItem.castAs<SubjectArea>(docFromREST, SubjectArea);

Toutes les hiérarchies d' objets SubjectArea auront la classe correcte.

Un cas d'utilisation / exemple; créer un service angulaire (classe de base abstraite à nouveau):

export abstract class BaseService<T extends ContentItem> {
  BASE_URL = 'http://host:port/';
  protected abstract http: Http;
  abstract path: string;
  abstract subClass: typeof ContentItem;

  cast(source: T): T {
    return ContentItem.castAs(source, this.subClass);
  }
  castAll(source: T[]): T[] {
    return ContentItem.castAllAs(source, this.subClass);
  }

  constructor() { }

  get(): Promise<T[]> {
    const value = this.http.get(`${this.BASE_URL}${this.path}`)
      .toPromise()
      .then(response => {
        const items: T[] = this.castAll(response.json());
        return items;
      });
    return value;
  }
}

L'utilisation devient très simple; créer un service Area:

@Injectable()
export class SubjectAreaService extends BaseService<SubjectArea> {
  path = 'area';
  subClass = SubjectArea;

  constructor(protected http: Http) { super(); }
}

La méthode get () du service renverra une promesse d'un tableau déjà converti en objets SubjectArea (hiérarchie entière)

Maintenant, disons, nous avons une autre classe:

export class OtherItem extends ContentItem {...}

La création d'un service qui récupère les données et les convertit dans la classe correcte est aussi simple que:

@Injectable()
export class OtherItemService extends BaseService<OtherItem> {
  path = 'other';
  subClass = OtherItem;

  constructor(protected http: Http) { super(); }
}
Normunds Kalnberzins
la source
2

Utilisez une classe étendue à partir d'une interface.

Alors:

Object.assign(
                new ToWhat(),
                what
              )

Et mieux:

Object.assign(
                    new ToWhat(),
                    <IDataInterface>what
                  )

ToWhat devient un contrôleur de DataInterface.

Sam
la source
1

https://jvilk.com/MakeTypes/

vous pouvez utiliser ce site pour générer un proxy pour vous. il génère une classe et peut analyser et valider votre objet JSON d'entrée.

Amir Mehrabi
la source
0

Dans le lates TS, vous pouvez faire comme ceci:

const isMyInterface = (val: any): val is MyInterface => {
  if (!val) { return false; }
  if (!val.myProp) { return false; }
  return true;
};

Et que l'utilisateur comme ça:

if (isMyInterface(data)) {
 // now data will be type of MyInterface
}
Jaroslav
la source
0

J'ai rencontré un besoin similaire. Je voulais quelque chose qui me donnera une transformation facile de / vers JSON qui provient d'un appel d'API REST vers / depuis une définition de classe spécifique. Les solutions que j'ai trouvées étaient insuffisantes ou destinées à réécrire le code de mes classes et à ajouter des annotations ou similaires.

Je voulais que quelque chose comme GSON soit utilisé en Java pour sérialiser / désérialiser les classes vers / depuis les objets JSON.

Combiné avec un besoin ultérieur, que le convertisseur fonctionnera également dans JS, j'ai fini d'écrire mon propre package.

Il a cependant, un peu de frais généraux. Mais au démarrage, il est très pratique pour l'ajout et l'édition.

Vous initialisez le module avec:

  1. schéma de conversion - permettant de mapper entre les champs et de déterminer comment la conversion sera effectuée
  2. Tableau de correspondance des classes
  3. Carte des fonctions de conversion - pour les conversions spéciales.

Ensuite, dans votre code, vous utilisez le module initialisé comme:

const convertedNewClassesArray : MyClass[] = this.converter.convert<MyClass>(jsonObjArray, 'MyClass');

const convertedNewClass : MyClass = this.converter.convertOneObject<MyClass>(jsonObj, 'MyClass');

ou, vers JSON:

const jsonObject = this.converter.convertToJson(myClassInstance);

Utilisez ce lien vers le package npm et également une explication détaillée sur la façon de travailler avec le module: json-class-converter

Également enveloppé pour une
utilisation angulaire dans: convertisseur de classe json angulaire

Doronm
la source
0

Passez l'objet tel quel au constructeur de classe; Pas de conventions ni de contrôles

interface iPerson {
   name: string;
   age: number;
}

class Person {
   constructor(private person: iPerson) { }

   toString(): string {
      return this.person.name + ' is ' + this.person.age;
   }  
}


// runs this as // 
const object1 = { name: 'Watson1', age: 64 };
const object2 = { name: 'Watson2' };            // age is missing

const person1 = new Person(object1);
const person2 = new Person(object2 as iPerson); // now matches constructor

console.log(person1.toString())  // Watson1 is 64
console.log(person2.toString())  // Watson2 is undefined
Lars Klingsten
la source
0

Vous pouvez utiliser ce package npm. https://www.npmjs.com/package/class-converter

Il est facile à utiliser, par exemple:

class UserModel {
  @property('i')
  id: number;

  @property('n')
  name: string;
}

const userRaw = {
  i: 1234,
  n: 'name',
};

// use toClass to convert plain object to class
const userModel = toClass(userRaw, UserModel);
// you will get a class, just like below one
// const userModel = {
//   id: 1234,
//   name: 'name',
// }
QC-guépard
la source
0

Personnellement, je trouve épouvantable que dactylographié ne permette pas à une définition de point de terminaison de spécifier le type de l'objet reçu. Comme il semble que ce soit effectivement le cas, je ferais ce que j'ai fait avec d'autres langages, c'est-à-dire que je séparerais l'objet JSON de la définition de classe et que la définition de classe utilise l'objet JSON comme seul membre de données .

Je méprise le code passe-partout, donc pour moi, il s'agit généralement d'obtenir le résultat souhaité avec le moins de code possible tout en préservant le type.

Considérez les définitions de structure d'objet JSON suivantes - ce serait ce que vous recevriez à un point de terminaison, ce ne sont que des définitions de structure, pas de méthodes.

interface IAddress {
    street: string;
    city: string;
    state: string;
    zip: string;
}

interface IPerson {
    name: string;
    address: IAddress;
}

Si nous considérons ce qui précède en termes orientés objet, les interfaces ci-dessus ne sont pas des classes car elles ne définissent qu'une structure de données. Une classe en termes OO définit les données et le code qui les opère.

Nous définissons donc maintenant une classe qui spécifie les données et le code qui les opère ...

class Person {
    person: IPerson;

    constructor(person: IPerson) {
        this.person = person;
    }

    // accessors
    getName(): string {
        return person.name;
    }

    getAddress(): IAddress {
        return person.address;
    }

    // You could write a generic getter for any value in person, 
    // no matter how deep, by accepting a variable number of string params

    // methods
    distanceFrom(address: IAddress): float {
        // Calculate distance from the passed address to this persons IAddress
        return 0.0;
    }
}

Et maintenant, nous pouvons simplement passer n'importe quel objet conforme à la structure IPerson et être en route ...

   Person person = new Person({
            name: "persons name",
            address: {
                street: "A street address",
                city: "a city",
                state: "a state",
                zip: "A zipcode"
            }
        });

De la même manière, nous pouvons maintenant traiter l'objet reçu à votre point d'extrémité avec quelque chose comme ...

Person person = new Person(req.body);    // As in an object received via a POST call

person.distanceFrom({ street: "Some street address", etc.});

Ceci est beaucoup plus performant et utilise la moitié de la mémoire de copie des données, tout en réduisant considérablement la quantité de code standard que vous devez écrire pour chaque type d'entité. Il repose simplement sur la sécurité des types fournie par TypeScript.

Rodney P. Barbati
la source
0

Utilisez la déclaration 'as':

const data = JSON.parse(response.data) as MyClass;
Daniel Valdebenito
la source
Cette technique a été mentionnée dans cette réponse il y a deux ans et, comme cela a été discuté ailleurs, n'ajoute aucune fonction pouvant être déclarée MyClass.
Heretic Monkey
-1

C'est une option simple et vraiment bonne

let person = "{"name":"Sam","Age":"30"}";

const jsonParse: ((key: string, value: any) => any) | undefined = undefined;
let objectConverted = JSON.parse(textValue, jsonParse);

Et puis vous aurez

objectConverted.name
SamazoOo
la source
-1

J'ai utilisé cette bibliothèque ici: https://github.com/pleerock/class-transformer

<script lang="ts">
    import { plainToClass } from 'class-transformer';
</script>

La mise en oeuvre:

private async getClassTypeValue() {
  const value = await plainToClass(ProductNewsItem, JSON.parse(response.data));
}

Parfois, vous devrez analyser les valeurs JSON pour plainToClass pour comprendre qu'il s'agit de données au format JSON

Mac Chibueze
la source
La bibliothèque 'class-transformer' est déjà suggérée dans une autre réponse ci-dessus stackoverflow.com/a/40042282/52277
Michael Freidgeim
-1

J'utilise Angular 6 sur le frontend et l'application Spring Boot sur le backend qui renvoie les objets Java. Tout ce que je dois faire est de définir une classe similaire sur l'application angulaire qui a des propriétés correspondantes, puis je peux accepter l'objet «comme» un objet de classe angulaire ( comp comme Company dans l'exemple ci-dessous).

Reportez-vous au code frontal ci-dessous par exemple. Faites-moi savoir dans les commentaires si quelque chose a besoin de plus de clarté.

  createCompany() {
    let company = new Company();
    company.name = this.companyName;

    this.companyService.createCompany(company).subscribe(comp => {
       if (comp !== null) {
        this.employerAdminCompany = comp as Company;
      }
    });
  }

où entreprise est un objet entité dans l'application Spring Boot et est également une classe en angulaire.

export class Company {
    public id: number;
    public name: string;
    public owner: User;
    public locations: Array<Location>;
}
Maneesh Parihar
la source