Come faccio a lanciare un oggetto JSON in una classe dattiloscritta


393

Ho letto un oggetto JSON da un server REST remoto. Questo oggetto JSON ha tutte le proprietà di una classe dattiloscritta (in base alla progettazione). Come posso trasmettere l'oggetto JSON ricevuto a un tipo var?

Non voglio popolare un var dattiloscritto (cioè avere un costruttore che accetta questo oggetto JSON). È grande e la copia di tutto attraverso l'oggetto secondario per oggetto secondario e la proprietà per proprietà richiederebbe molto tempo.

Aggiornamento: puoi comunque lanciarlo su un'interfaccia dattiloscritta!


puoi usare github.com/vojtechhabarta/typescript-generator per generare interfacce TypeScript nel caso in cui il tuo JSON sia mappato usando le classi Java
Vojta

Ho codificato una piccola libreria di casting: sulphur-blog.azurewebsite.net/typescript-mini-cast-library
Camille Wintz,

1
Oggetto JSON = oggetto Notazione oggetto JavaScript. Non ha abbastanza oggetti lì dentro, dico che ne aggiungiamo un altro paio per una buona misura.
bug-a-lot

1
Ho creato uno strumento per questo beshanoe.github.io/json2ts
beshanoe

La creazione di un prototipo di classe TypeScript per definire il tuo oggetto non danneggerà il vero codice di produzione. Dai un'occhiata al file JS compilato, tutte le definizioni verranno rimosse, poiché non fanno parte di JS.
FisNaN,

Risposte:


167

Non è possibile eseguire semplicemente il cast di un risultato JavaScript semplice da una richiesta Ajax in un'istanza di classe JavaScript / TypeScript prototipica. Esistono diverse tecniche per farlo e generalmente comportano la copia dei dati. A meno che non si crei un'istanza della classe, non avrà alcun metodo o proprietà. Rimarrà un semplice oggetto JavaScript.

Mentre se avessi a che fare solo con i dati, potresti semplicemente eseguire un cast su un'interfaccia (poiché è puramente una struttura temporale di compilazione), ciò richiederebbe l'uso di una classe TypeScript che utilizza l'istanza dei dati ed esegue operazioni con tali dati.

Alcuni esempi di copia dei dati:

  1. Copia dell'oggetto AJAX JSON nell'oggetto esistente
  2. Analizzare la stringa JSON in un prototipo di oggetto particolare in JavaScript

In sostanza, avresti semplicemente:

var d = new MyRichObject();
d.copyInto(jsonResult);

Sono d'accordo con la tua risposta. Inoltre, anche se non sono in grado di cercarlo e provarlo in questo momento, penso che quei due passaggi potrebbero essere combinati dando una funzione di sveglia come parametro a JSON.parse(). Entrambi dovrebbero ancora essere fatti, ma sintatticamente potrebbero essere combinati.
JAAulde,

Certo, potrebbe funzionare anche questo: non ho idea di come sarebbe più efficiente, dato che dovrebbe chiamare una funzione aggiuntiva per ogni proprietà.
WiredPrairie,

Sicuramente non la risposta che stavo cercando :( Per curiosità, perché è questo? Mi sembra il modo in cui javascript funziona che dovrebbe essere fattibile.
David Thielen,

No, non funziona in TypeScript perché non esiste un modo semplice in JavaScript per farlo.
WiredPrairie,

1
Che direObject.setPrototypeOf
Petah,

102

Ho avuto lo stesso problema e ho trovato una libreria che fa il lavoro: https://github.com/pleerock/class-transformer .

Funziona così:

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

Supporta i bambini nidificati ma devi decorare il membro della tua classe.


14
Questa piccola libreria brillante l'ha risolta perfettamente con il minimo sforzo (non dimenticare le tue @Typeannotazioni, però). Questa risposta merita più credito.
Benny Bottema,

Oh wow !, questa libreria non è così piccola che forse ha tutto ciò di cui hai bisogno, ti consente persino di controllare la trasformazione con il decoratore @transform: D
Diego Fernando Murillo Valenci,

3
Si noti che questa libreria non viene più più gestita. Non funziona più con Angular5 + e dato che non stanno nemmeno unendo più le richieste pull, non penso che lavoreranno presto. È una grande biblioteca però.
Kent,

Esiste una soluzione alternativa per Angular5 + (in realtà è un bug angolare): github.com/typestack/class-transformer/issues/108
Pak

2
Funziona bene in Angular 6 (almeno per il mio caso d'uso che è solo per mappare letteralmente JSON <=> Classe)
tftd

54

In TypeScript puoi fare un'asserzione di tipo usando un'interfaccia e generici in questo modo:

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

Dove ILocationMap descrive la forma dei tuoi dati. Il vantaggio di questo metodo è che il tuo JSON potrebbe contenere più proprietà ma la forma soddisfa le condizioni dell'interfaccia.

Spero che aiuti!


49
FYI: È un'affermazione di tipo, non un cast.
WiredPrairie il

6
Vedi qui per la differenza tra un'asserzione di tipo e un cast .
Stefan Hanke,

7
Dove posso trovare Utilities.JSONLoader?
HypeXR,

22
Ma non avrà alcun metodo, come indicato nella risposta.
Martín Coll,

1
@StefanHanke sembra che l'URL sia leggermente cambiato: "Digita asserzione vs. casting"
ruffin

37

Se stai usando ES6, prova questo:

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

Ma in questo modo non funzionerà sull'oggetto nido , purtroppo.


4
Lo hanno chiesto in Typescript.
joe.feser

HI @ joe.feser, menziono ES6 perché in questo modo è richiesto il metodo "Object.assign".
migcoder

1
Se manca il costruttore predefinito, è possibile creare l'istanza di destinazione Object.create(MyClass.prototype), ignorando del tutto il costruttore.
Marcello,

Maggiori spiegazioni sugli oggetti nidificati limitazione vedono in stackoverflow.com/questions/22885995/...
Michael Freidgeim

28

Ho trovato un articolo molto interessante sul casting generico di JSON in una classe Typescript:

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

Si finisce con il seguente codice:

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

20

TLDR: una fodera

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

La risposta dettagliata

Vorrei non raccomandare l'approccio Object.assign, in quanto si può impropriamente lettiera l'istanza di classe con le proprietà irrilevanti (così come definiti chiusure) che non sono stati dichiarati all'interno della classe stessa.

Nella classe in cui stai cercando di deserializzare, assicurerei che siano definite tutte le proprietà che vuoi deserializzare (null, array vuoto, ecc.). Definendo le proprietà con i valori iniziali si espone la loro visibilità quando si tenta di iterare i membri della classe a cui assegnare i valori (vedere il metodo di deserializzazione di seguito).

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

Nell'esempio sopra, ho semplicemente creato un metodo di deserializzazione. In un esempio del mondo reale, lo avrei centralizzato in una classe di base o metodo di servizio riutilizzabile.

Ecco come utilizzare questo in qualcosa come un http resp ...

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

Se tslint / ide si lamenta dell'incompatibilità del tipo di argomento, basta inserire l'argomento nello stesso tipo usando parentesi angolari <YourClassName>, ad esempio:

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

Se hai membri di una classe che sono di un tipo specifico (ovvero: istanza di un'altra classe), puoi farli trasmettere in istanze tipizzate tramite metodi 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'esempio sopra deserializzerà acct e l'elenco delle attività nelle rispettive istanze di classe.


Ricevo questo messaggio di errore: non è possibile convertire il tipo "{nome: stringa, età: numero, id: numero}" nel tipo "Persona". La proprietà 'id' è privata nel tipo 'Persona' ma non nel tipo '{nome: stringa, età: numero, ID: numero}'
utiq

Come dovrei usarlo con enums? Devo utilizzare l'approccio di tipo specifico e aggiungere getter e setter per esso?
Tadija Bagarić,

@TimothyParez Quando imposti le attività?
Kay,

Ho provato a fare qualcosa di simile ma il mio array di attività è vuoto quando console.log persona.
Kay,

Per compilare questo ho dovuto aggiungere una firma indice alla classe: export class Person {[key: string]: any (...)}
Asimov

18

Supponendo che il json abbia le stesse proprietà della classe dattiloscritta, non è necessario copiare le proprietà Json sull'oggetto dattiloscritto. Dovrai solo costruire il tuo oggetto Typescript passando i dati json nel costruttore.

Nel tuo callback Ajax, ricevi una società:

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

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

Per farlo funzionare:

1) Aggiungi un costruttore nella tua classe Typescript che accetta i dati json come parametro. In quel costruttore di estendere l'oggetto JSON con jQuery, in questo modo: $.extend( this, jsonData). $ .extend consente di mantenere i prototipi javascript durante l'aggiunta delle proprietà dell'oggetto json.

2) Nota che dovrai fare lo stesso per gli oggetti collegati. Nel caso dei dipendenti nell'esempio, si crea anche un costruttore che prende la parte dei dati json per i dipendenti. Si chiama $ .map per tradurre i dipendenti json in dattiloscrivere oggetti Employee.

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

Questa è la migliore soluzione che ho trovato quando ho a che fare con classi Typescript e oggetti json.


Preferisco questa soluzione rispetto all'implementazione e alla manutenzione delle interfacce, perché le mie applicazioni Angular2 hanno un modello di applicazione reale che potrebbe essere diverso dal modello dei servizi Web utilizzato dalla mia applicazione. Può avere dati privati ​​e proprietà calcolate.
Anthony Brenelière,

7
Usare JQuery nei progetti angolari è un'idea terribile. E se i tuoi modelli contengono un sacco di funzioni su di essi, non sono più modelli.
Assapora il

1
@Davor Intendi POJO o modello? POJO (fondamentalmente oggetti semplici) non hanno funzioni, mentre modello è un termine più ampio e include un repository. Il modello del repository, a differenza di POJO, riguarda le funzioni, ma è ancora un modello.
forsberg,

@Davor: usare JQuery nei progetti angolari non è una cattiva idea, purché non lo si usi per manipolare il DOM, che è davvero una terribile idea. Uso qualsiasi libreria di cui ho bisogno per i miei progetti angolari, e per jQuery non è un'opzione perché il mio progetto usa SignalR che dipende da esso. Nel caso di classi, ora utilizzate da javascript ES6, si accede ai dati con proprietà che sono funzioni che incapsulano il modo in cui i dati vengono archiviati in memoria. Per i costruttori, esiste un modo corretto di utilizzare le fabbriche.
Anthony Brenelière,

L'OP riguarda chiaramente modelli di dati semplici per REST. Lo stai rendendo inutilmente complicato, ragazzi. E sì, puoi usare Jquery per cose aggiuntive, ma stai importando una libreria enorme per usarne l'1%. Questo è un odore di codice se ne avessi mai visto uno.
Assapora il

18

Non c'è ancora nulla per verificare automaticamente se l'oggetto JSON che hai ricevuto dal server ha le proprietà dell'interfaccia del typescript previste (leggi è conforme alle). Ma puoi usare le Guardie di tipo definite dall'utente

Considerando la seguente interfaccia e un oggetto json sciocco (avrebbe potuto essere di qualsiasi tipo):

interface MyInterface {
    key: string;
 }

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

Tre modi possibili:

A. Digitare Assertion o semplice cast statico posto dopo la variabile

const myObject: MyInterface = json as MyInterface;

B. Cast statico semplice, prima della variabile e tra i diamanti

const myObject: MyInterface = <MyInterface>json;

C. Cast dinamico avanzato, controlli te stesso la struttura dell'oggetto

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}'.`);
}

Puoi giocare con questo esempio qui

Si noti che la difficoltà qui è scrivere la isMyInterfacefunzione. Spero che TS prima o poi aggiungerà un decoratore per esportare la tipizzazione complessa nel runtime e lasciare che il runtime controlli la struttura dell'oggetto quando necessario. Per ora, è possibile utilizzare un validatore di schemi json il cui scopo è approssimativamente lo stesso oppure questo generatore di funzioni di controllo del tipo di runtime


1
Penso che la tua risposta dovrebbe essere al massimo
afzalex il

Decisamente. Questa è la risposta migliore che si basa direttamente sul sito Web ts.
Burak Karakuş,

15

Nel mio caso funziona. Ho usato le funzioni Object.assign (target, fonti ...) . Innanzitutto, la creazione dell'oggetto corretto, quindi copia i dati dall'oggetto json sulla destinazione. Esempio:

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

E un esempio più avanzato di utilizzo. Un esempio che utilizza l'array.

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

Cosa succede quando hai una proprietà privata?
prasanthv,

Perché l'oggetto jsonUser non è una classe User. Senza operazione Object.assign (u, jsonUsers); Non è possibile utilizzare la funzione getId (). Solo dopo l'assegnazione si ottiene un oggetto Utente valido in cui è possibile utilizzare la funzione getId (). La funzione getId () è solo per l'esempio che l'operazione è andata a buon fine.
Adam111p,

puoi saltare la temp var - basta farethis.users[i] = new User(); Object.assign(this.users[i], users[i])
cyptus il

o ancora meglio utilizzare il valore restituito:this.users[i] = Object.assign(new User(), users[i]);
cyptus

Questa versione lunga è solo a scopo illustrativo. Puoi abbreviare il codice quanto vuoi :)
Adam111p

6

Mentre non è casting per sé; Ho trovato https://github.com/JohnWhiteTB/TypedJSON come un'alternativa 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"

1
Non è casting, cosa fa davvero?
DanielM,

1
Questa soluzione richiede moltissime annotazioni. Non c'è davvero un modo più semplice?
JPNotADragon,

3

Puoi creare un interfacetipo ( SomeType) e lanciare l'oggetto in quello.

const typedObject: SomeType = <SomeType> responseObject;

3

Se devi trasmettere il tuo oggetto json a una classe dattiloscritta e avere i suoi metodi di istanza disponibili nell'oggetto risultante devi usare Object.setPrototypeOf, come ho fatto nel seguente frammento di codice:

Object.setPrototypeOf(jsonObject, YourTypescriptClass.prototype)

2

Una vecchia domanda con risposte per lo più corrette, ma non molto efficienti. Questo è quello che propongo:

Creare una classe di base che contiene il metodo init () e i metodi di cast statici (per un singolo oggetto e un array). I metodi statici potrebbero essere ovunque; la versione con la classe base e init () consente successivamente facili estensioni.

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() { }
}

Meccanici simili (con assegnazione () ) sono stati menzionati nel post @ Adam111p. Solo un altro modo (più completo) per farlo. @Timothy Perez è critico nei confronti di assegnato () , ma imho che qui è del tutto appropriato.

Implementare una classe derivata (reale):

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

Ora possiamo lanciare un oggetto recuperato dal servizio:

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

Tutta la gerarchia degli oggetti SubjectArea avrà la classe corretta.

Un caso d'uso / esempio; creare un servizio angolare (di nuovo la classe base astratta):

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'utilizzo diventa molto semplice; creare un servizio di area:

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

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

Il metodo get () del servizio restituirà una Promessa di una matrice già lanciata come oggetti SubjectArea (intera gerarchia)

Ora diciamo, abbiamo un'altra classe:

export class OtherItem extends ContentItem {...}

La creazione di un servizio che recupera i dati e li lancia nella classe corretta è semplice come:

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

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

2

Utilizzare una classe estesa da un'interfaccia.

Poi:

Object.assign(
                new ToWhat(),
                what
              )

E meglio:

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

ToWhat diventa un controller di DataInterface.



0

Nelle ultime TS puoi fare così:

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

E rispetto all'utente in questo modo:

if (isMyInterface(data)) {
 // now data will be type of MyInterface
}

0

Mi sono imbattuto in un bisogno simile. Volevo qualcosa che mi desse una facile trasformazione da / a JSON che provenga da una chiamata API REST a / dalla definizione di classe specifica. Le soluzioni che ho trovato erano insufficienti o pensate per riscrivere il codice delle mie classi e aggiungere annotazioni o simili.

Volevo qualcosa come GSON utilizzato in Java per serializzare / deserializzare le classi da / verso oggetti JSON.

In combinazione con un'esigenza successiva, che il convertitore funzionerà anche in JS, ho finito di scrivere il mio pacchetto.

Tuttavia, ha un po 'di spese generali. Ma quando è avviato è molto conveniente aggiungere e modificare.

Si inizializza il modulo con:

  1. schema di conversione: consente di mappare tra i campi e determinare come verrà eseguita la conversione
  2. Matrice della mappa delle classi
  3. Mappa delle funzioni di conversione - per conversioni speciali.

Quindi nel tuo codice, usi il modulo inizializzato come:

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

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

oppure, a JSON:

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

Usa questo link al pacchetto npm e anche una spiegazione dettagliata su come lavorare con il modulo: json-class-converter

Lo ha anche avvolto per l'
uso angolare in: convertitore angolare-json-class


0

Passa l'oggetto così com'è al costruttore della classe; Nessuna convenzione o controllo

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

0

Puoi usare questo pacchetto npm. https://www.npmjs.com/package/class-converter

È facile da usare, ad esempio:

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',
// }

0

Personalmente trovo spaventoso che il dattiloscritto non consenta ad una definizione di endpoint di specificare il tipo di oggetto che viene ricevuto. Poiché sembra che sia effettivamente così, farei ciò che ho fatto con altri linguaggi, ovvero separerei l'oggetto JSON dalla definizione di classe e fare in modo che la definizione di classe utilizzi l'oggetto JSON come unico membro di dati .

Disprezzo il codice boilerplate, quindi per me di solito si tratta di ottenere il risultato desiderato con la minima quantità di codice mantenendo il tipo.

Considera le seguenti definizioni della struttura di oggetti JSON: queste sarebbero quelle che riceveresti a un endpoint, sono solo definizioni di struttura, nessun metodo.

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

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

Se pensiamo a quanto sopra in termini orientati agli oggetti, le interfacce di cui sopra non sono classi perché definiscono solo una struttura di dati. Una classe in termini di OO definisce i dati e il codice che opera su di essi.

Quindi ora definiamo una classe che specifica i dati e il codice che vi opera ...

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

E ora possiamo semplicemente passare qualsiasi oggetto conforme alla struttura di IPerson ed essere sulla nostra strada ...

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

Allo stesso modo ora possiamo elaborare l'oggetto ricevuto sul tuo endpoint con qualcosa lungo le linee di ...

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

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

Questo è molto più performante e utilizza metà della memoria della copia dei dati, riducendo in modo significativo la quantità di codice del boilerplate che devi scrivere per ogni tipo di entità. Si basa semplicemente sulla sicurezza dei tipi fornita da TypeScript.



-1

Questa è un'opzione semplice e davvero buona

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

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

E poi avrai

objectConverted.name

-1

Ho usato questa libreria qui: https://github.com/pleerock/class-transformer

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

Implementazione:

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

A volte dovrai analizzare i valori JSON per plainToClass per capire che si tratta di dati formattati JSON


La libreria di 'class-trasformatore' è già suggerita in altra risposta sopra stackoverflow.com/a/40042282/52277
Michael Freidgeim

-1

Sto usando Angular 6 sul frontend e l'applicazione Spring Boot sul backend che restituisce Java Objects. Tutto quello che devo fare è definire una classe simile sull'applicazione angolare che abbia proprietà corrispondenti e quindi posso accettare l'oggetto "come" un oggetto di classe angolare ( comp come Azienda nell'esempio seguente).

Fare riferimento al codice front-end di seguito, ad esempio. Fammi sapere nei commenti se qualcosa ha bisogno di più chiarezza.

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

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

dove company è un oggetto entità nell'app Spring Boot ed è anche una classe in Angular.

export class Company {
    public id: number;
    public name: string;
    public owner: User;
    public locations: Array<Location>;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.