¿Cómo convierto un objeto JSON en una clase de mecanografiado

393

Leí un objeto JSON de un servidor REST remoto. Este objeto JSON tiene todas las propiedades de una clase de mecanografiado (por diseño). ¿Cómo envío ese objeto JSON recibido a un tipo var?

No quiero rellenar un var de mecanografiado (es decir, tener un constructor que tome este objeto JSON). Es grande y copiar todo en un subobjeto por subobjeto y propiedad por propiedad llevaría mucho tiempo.

Actualización: ¡Sin embargo, puede enviarlo a una interfaz de mecanografía!

David Thielen
fuente
puede usar github.com/vojtechhabarta/typescript-generator para generar interfaces TypeScript en caso de que su JSON se asigne utilizando clases Java
Vojta
He codificado una pequeña biblioteca de casting: sulphur-blog.azurewebsites.net/typescript-mini-cast-library
Camille Wintz
1
Objeto JSON = Objeto de notación de objetos JavaScript. No tiene suficientes objetos allí, digo que agreguemos un par más por si acaso.
bug-a-lot el
1
He creado una herramienta para este beshanoe.github.io/json2ts
beshanoe
Crear una clase prototipo de TypeScript para definir su objeto no dañará el código de producción real. Eche un vistazo al archivo JS compilado, todas las definiciones se eliminarán, ya que no son parte de JS.
FisNaN

Respuestas:

167

No puede simplemente transmitir un resultado de JavaScript simple desde una solicitud de Ajax a una instancia prototipo de clase JavaScript / TypeScript. Existen varias técnicas para hacerlo, y generalmente implican copiar datos. A menos que cree una instancia de la clase, no tendrá ningún método o propiedades. Seguirá siendo un simple objeto JavaScript.

Si bien solo se tratara de datos, simplemente podría hacer una conversión a una interfaz (ya que es puramente una estructura de tiempo de compilación), esto requeriría que use una clase TypeScript que use la instancia de datos y realice operaciones con esos datos.

Algunos ejemplos de copia de datos:

  1. Copiar objeto AJAX JSON en un objeto existente
  2. Analizar la cadena JSON en un prototipo de objeto particular en JavaScript

En esencia, simplemente:

var d = new MyRichObject();
d.copyInto(jsonResult);
WiredPrairie
fuente
Estoy de acuerdo con tu respuesta Además, aunque no estoy en un lugar para buscarlo y probarlo en este momento, creo que esos dos pasos podrían combinarse dando una función de activación como parámetro JSON.parse(). Ambos aún tendrían que hacerse, pero sintácticamente podrían combinarse.
JAAulde
Claro, eso también podría funcionar: sin embargo, no tengo idea de si sería más eficiente, ya que necesitaría llamar a una llamada de función adicional para cada propiedad.
WiredPrairie
Definitivamente no es la respuesta que estaba buscando :( Por curiosidad, ¿por qué es esto? Me parece la forma en que funciona JavaScript que esto debería ser factible.
David Thielen
No, no funciona en TypeScript porque no hay una manera simple en JavaScript para hacer esto.
WiredPrairie
1
¿Qué hay deObject.setPrototypeOf
Petah
102

Tuve el mismo problema y encontré una biblioteca que hace el trabajo: https://github.com/pleerock/class-transformer .

Funciona así :

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

Es compatible con niños anidados, pero debe decorar el miembro de su clase.

Pak
fuente
14
Esta pequeña biblioteca brillante lo resolvió perfectamente con el menor esfuerzo (sin @Typeembargo, no olvide sus anotaciones). Esta respuesta merece más crédito.
Benny Bottema
¡Oh, vaya !, esta biblioteca no es tan pequeña, tiene tal vez todo lo que necesitas, incluso te permite controlar la transformación con el decorador @transform: D
Diego Fernando Murillo Valenci
3
Tenga en cuenta que esta biblioteca ya no se mantiene. Ya no funciona con Angular5 + y como ya ni siquiera están fusionando solicitudes de extracción, no creo que vayan a trabajar en eso pronto. Sin embargo, es una gran biblioteca.
kentor
Hay una solución para Angular5 + (en realidad es un error angular): github.com/typestack/class-transformer/issues/108
Pak
2
Esto funciona bien en Angular 6 (al menos para mi caso de uso, que es solo para mapear literalmente JSON <=> Clase)
tftd
54

En TypeScript puede hacer una aserción de tipo usando una interfaz y genéricos de la siguiente manera:

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

Donde ILocationMap describe la forma de sus datos. La ventaja de este método es que su JSON podría contener más propiedades pero la forma satisface las condiciones de la interfaz.

¡Espero que eso ayude!

user756310
fuente
49
FYI: Es una afirmación de tipo, no un elenco.
WiredPrairie
66
Vea aquí la diferencia entre una aserción de tipo y un elenco .
Stefan Hanke
77
¿Dónde puedo encontrar Utilities.JSONLoader?
HypeXR
22
Pero no tendrá ningún método, como se menciona en la respuesta.
Martín Coll
1
@StefanHanke parece que la URL ha cambiado ligeramente: "Tipo de afirmación frente a casting"
ruffin
37

Si está utilizando ES6, intente esto:

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

Pero de esta manera no funcionará en el objeto del nido , lamentablemente.

migcoder
fuente
44
Lo pidieron en mecanografiado.
joe.feser
Hola @ joe.feser, menciono ES6 porque de esta manera se requiere el método 'Object.assign'.
migcoder
1
En caso de que falte el constructor predeterminado, se puede crear la instancia de destino Object.create(MyClass.prototype), evitando el constructor por completo.
Marcello
Más explicaciones sobre la limitación de objetos anidados en stackoverflow.com/questions/22885995/…
Michael Freidgeim
28

Encontré un artículo muy interesante sobre la conversión genérica de JSON a una clase de tipografía:

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

Terminas con el siguiente código:

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
Philip
fuente
20

TLDR: un revestimiento

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

La respuesta detallada

Yo no recomendar el enfoque Object.assign, ya que puede inapropiadamente la basura su instancia de clase con propiedades irrelevantes (así como los cierres definidos) que se declaró no dentro de la propia clase.

En la clase en la que intenta deserializarse, me aseguraría de que se definan las propiedades que desea deserializar (nula, matriz vacía, etc.). Al definir sus propiedades con valores iniciales, expone su visibilidad cuando intenta iterar a los miembros de la clase para asignarles valores (consulte el método de deserialización a continuación).

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

En el ejemplo anterior, simplemente creé un método de deserialización. En un ejemplo del mundo real, lo tendría centralizado en una clase base reutilizable o método de servicio.

Aquí es cómo utilizar esto en algo así como un http resp ...

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

Si tslint / ide se queja de que el tipo de argumento es incompatible, simplemente convierta el argumento en el mismo tipo usando paréntesis angulares <YourClassName>, por ejemplo:

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

Si tiene miembros de clase que son de un tipo específico (también conocido como: instancia de otra clase), puede convertirlos en instancias escritas a través de métodos 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));
  }
}

El ejemplo anterior deserializará tanto el acct como la lista de tareas en sus respectivas instancias de clase.

Timothy Perez
fuente
Recibo este mensaje de error: Tipo '{nombre: cadena, edad: número, id: número}' no se puede convertir al tipo 'Persona'. La propiedad 'id' es privada en el tipo 'Persona' pero no en el tipo '{nombre: cadena, edad: número, id: número}'
utiq
¿Cómo debo usar esto con las enumeraciones? ¿Tengo que usar el enfoque de tipo específico y agregar getter y setter para él?
Tadija Bagarić
@TimothyParez ¿Cuándo establece las tareas?
Kay
Traté de hacer algo similar, pero mi matriz de tareas está vacía cuando console.log person.
Kay
Para que esto se compilara, tuve que agregar una firma de índice a la clase: export class Person {[key: string]: any (...)}
Asimov
18

Suponiendo que json tiene las mismas propiedades que su clase de mecanografiado, no tiene que copiar sus propiedades de Json a su objeto de mecanografiado. Solo tendrá que construir su objeto Typecript pasando los datos json en el constructor.

En su devolución de llamada ajax, recibe una empresa:

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

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

Para hacer que eso funcione:

1) Agregue un constructor en su clase de Script mecanografiado que tome los datos json como parámetro. En ese constructor a ampliar su objeto JSON con jQuery, así: $.extend( this, jsonData). $ .extend permite mantener los prototipos de JavaScript al tiempo que agrega las propiedades del objeto json.

2) Tenga en cuenta que tendrá que hacer lo mismo para los objetos vinculados. En el caso de Empleados en el ejemplo, también crea un constructor que toma la porción de los datos json para los empleados. Llama a $ .map para traducir empleados json a objetos de empleados mecanografiados.

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

Esta es la mejor solución que encontré al tratar con clases de Tipos de letra y objetos json.

Anthony Brenelière
fuente
Prefiero esta solución en lugar de implementar y mantener interfaces, porque mis aplicaciones Angular2 tienen un modelo de aplicación real que puede ser diferente al modelo de los servicios web que consume mi aplicación. Puede tener datos privados y propiedades calculadas.
Anthony Brenelière
77
Usar JQuery en proyectos angulares es una idea terrible. Y si sus modelos contienen muchas funciones, ya no son modelos.
Davor
1
@Davor ¿Te refieres a POJO o modelo? POJO (básicamente objetos simples) no tiene funciones, mientras que modelo es un término más amplio e incluye repositorio. El patrón de repositorio, en contraste con POJO, se trata de funciones, pero sigue siendo modelo.
forsberg
@Davor: usar JQuery en proyectos angulares no es una mala idea siempre y cuando no lo uses para manipular el DOM, lo cual es una idea terrible. Utilizo cualquier biblioteca que necesito para mis proyectos angulares, y para jQuery no es una opción porque mi proyecto usa SignalR que depende de él. En el caso de las clases, ahora utilizadas por javascript ES6, se accede a los datos con propiedades que son funciones que encapsulan la forma en que los datos se almacenan en la memoria. Para los constructores, hay una forma adecuada de usar fábricas.
Anthony Brenelière
El OP se trata claramente de modelos de datos simples para REST. Lo están haciendo innecesariamente complicado, muchachos. Y sí, puedes usar Jquery para cosas adicionales, pero estás importando una biblioteca masiva para usar el 1%. Ese es un olor a código si alguna vez he visto uno.
Davor
18

Todavía no hay nada que verifique automáticamente si el objeto JSON que recibió del servidor tiene las propiedades de interfaz esperadas (leer se ajustan a). Pero puede usar protectores de tipo definidos por el usuario

Teniendo en cuenta la siguiente interfaz y un objeto json tonto (podría haber sido de cualquier tipo):

interface MyInterface {
    key: string;
 }

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

Tres formas posibles:

A. Tipo de aserción o molde estático simple colocado después de la variable

const myObject: MyInterface = json as MyInterface;

B. Molde estático simple, antes de la variable y entre diamantes.

const myObject: MyInterface = <MyInterface>json;

C. Reparto dinámico avanzado, comprueba usted mismo la estructura del objeto.

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

Puedes jugar con este ejemplo aquí

Tenga en cuenta que la dificultad aquí es escribir la isMyInterfacefunción. Espero que TS agregue un decorador tarde o temprano para exportar la escritura compleja al tiempo de ejecución y dejar que el tiempo de ejecución verifique la estructura del objeto cuando sea necesario. Por ahora, puede usar un validador de esquema json cuyo propósito es aproximadamente el mismo O este generador de función de verificación de tipo de tiempo de ejecución

Flavien Volken
fuente
1
Tu respuesta debería estar en la cima, creo
afzalex
Seguro. Esta es la mejor respuesta que se basa directamente en el sitio web de ts.
Burak Karakuş
15

En mi caso funciona. Usé las funciones Object.assign (target, sources ...) . Primero, la creación del objeto correcto, luego copia los datos del objeto json al destino. Ejemplo:

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

Y un ejemplo más avanzado de uso. Un ejemplo usando la matriz.

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
fuente
¿Qué sucede cuando tienes una propiedad privada?
prasanthv
Porque el objeto jsonUser no es una clase de usuario. Sin operación Object.assign (u, jsonUsers); No puede usar la función getId (). Solo después de asignar, obtiene un objeto Usuario válido en el que puede usar la función getId (). La función getId () es solo para el ejemplo de que la operación fue exitosa.
Adam111p
puedes saltarte la temperatura var - solo hazlothis.users[i] = new User(); Object.assign(this.users[i], users[i])
cyptus
o incluso mejor hacer uso del valor de retorno:this.users[i] = Object.assign(new User(), users[i]);
cyptus
Esta versión larga es solo para explicación. Puedes acortar el código tanto como quieras :)
Adam111p
6

Si bien no está lanzando per se; He encontrado https://github.com/JohnWhiteTB/TypedJSON como una alternativa útil.

@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
fuente
1
No es casting, ¿qué hace realmente?
DanielM
1
Esta solución requiere una gran cantidad de anotaciones. ¿Realmente no hay una manera más fácil?
JPNotADragon
3

Puede crear uno interfacede su tipo ( SomeType) y convertir el objeto en eso.

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

Si necesita convertir su objeto json en una clase de mecanografiado y tener sus métodos de instancia disponibles en el objeto resultante que necesita usar Object.setPrototypeOf, como hice en el fragmento de código a continuación:

Object.setPrototypeOf(jsonObject, YourTypescriptClass.prototype)
s_bighead
fuente
2

Una vieja pregunta con respuestas en su mayoría correctas, pero no muy eficientes. Esto lo propongo:

Cree una clase base que contenga el método init () y los métodos de conversión estática (para un solo objeto y una matriz). Los métodos estáticos pueden estar en cualquier parte; La versión con la clase base y init () permite extensiones fáciles después.

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

Mecánicos similares (con asignar () ) se han mencionado en la publicación @ Adam111p. Solo otra forma (más completa) de hacerlo. @Timothy Perez es crítico de asignar () , pero en mi opinión, es completamente apropiado aquí.

Implemente una clase derivada (la real):

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

Ahora podemos lanzar un objeto recuperado del servicio:

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

Toda la jerarquía de objetos SubjectArea tendrá la clase correcta.

Un caso de uso / ejemplo; crear un servicio angular (clase base abstracta nuevamente):

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

El uso se vuelve muy simple; crear un servicio de área:

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

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

El método get () del servicio devolverá una Promesa de una matriz ya convertida como objetos SubjectArea (jerarquía completa)

Ahora digamos, tenemos otra clase:

export class OtherItem extends ContentItem {...}

Crear un servicio que recupera datos y se convierte en la clase correcta es tan simple como:

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

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

Use una clase extendida desde una interfaz.

Entonces:

Object.assign(
                new ToWhat(),
                what
              )

Y lo mejor:

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

ToWhat se convierte en un controlador de DataInterface.

Sam
fuente
1

https://jvilk.com/MakeTypes/

puede usar este sitio para generar un proxy para usted. genera una clase y puede analizar y validar su objeto JSON de entrada.

Amir Mehrabi
fuente
0

En los últimos TS puedes hacer así:

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

Y que usuario como este:

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

Me encontré con una necesidad similar. Quería algo que me diera una fácil transformación de / a JSON que provenga de una llamada API REST a / desde una definición de clase específica. Las soluciones que he encontrado son insuficientes o pretenden reescribir el código de mis clases y agregar anotaciones o similares.

Quería algo como GSON se usa en Java para serializar / deserializar clases a / desde objetos JSON.

Combinado con una necesidad posterior, de que el convertidor funcionará también en JS, terminé de escribir mi propio paquete.

Sin embargo, tiene un poco de gastos generales. Pero cuando se inicia, es muy conveniente agregar y editar.

Inicializa el módulo con:

  1. esquema de conversión: permite asignar entre campos y determinar cómo se realizará la conversión
  2. Mapeo de clases
  3. Mapa de funciones de conversión: para conversiones especiales.

Luego, en su código, usa el módulo inicializado como:

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

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

o, a JSON:

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

Use este enlace al paquete npm y también una explicación detallada de cómo trabajar con el módulo: json-class-converter

También lo envolvió para
uso angular en: angular-json-class-converter

Doronm
fuente
0

Pase el objeto como está al constructor de la clase; Sin convenciones ni controles

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
fuente
0

Puedes usar este paquete npm. https://www.npmjs.com/package/class-converter

Es fácil de usar, por ejemplo:

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-Cheetah
fuente
0

Personalmente, me parece terrible que el mecanografiado no permita que una definición de punto final especifique el tipo de objeto que se recibe. Como parece que este es el caso, haría lo que he hecho con otros idiomas, y es que separaría el objeto JSON de la definición de clase, y haría que la definición de clase usara el objeto JSON como su único miembro de datos .

Desprecio el código repetitivo, por lo que para mí generalmente se trata de obtener el resultado deseado con la menor cantidad de código mientras se conserva el tipo.

Considere las siguientes definiciones de estructura de objeto JSON: estas serían las que recibiría en un punto final, son solo definiciones de estructura, no métodos.

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

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

Si pensamos en lo anterior en términos orientados a objetos, las interfaces anteriores no son clases porque solo definen una estructura de datos. Una clase en términos OO define los datos y el código que opera en ellos.

Así que ahora definimos una clase que especifica datos y el código que opera en ellos ...

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

Y ahora simplemente podemos pasar cualquier objeto conforme a la estructura de IPerson y seguir nuestro camino ...

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

De la misma manera, ahora podemos procesar el objeto recibido en su punto final con algo parecido a ...

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

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

Esto es mucho más eficaz y utiliza la mitad de la memoria de la copia de datos, al tiempo que reduce significativamente la cantidad de código repetitivo que debe escribir para cada tipo de entidad. Simplemente se basa en la seguridad de tipo proporcionada por TypeScript.

Rodney P. Barbati
fuente
0

Utilice la declaración 'como':

const data = JSON.parse(response.data) as MyClass;
Daniel Valdebenito
fuente
Esta técnica se mencionó en esta respuesta de dos años anteriores y, como se ha discutido en otra parte, no agrega ninguna función que pueda declararse MyClass.
Heretic Monkey
-1

Esta es una opción simple y realmente buena

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

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

Y luego tendrás

objectConverted.name
SamazoOo
fuente
-1

Usé esta biblioteca aquí: https://github.com/pleerock/class-transformer

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

Implementación:

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

A veces tendrá que analizar los valores JSON para plainToClass para comprender que se trata de datos con formato JSON

Mac Chibueze
fuente
La biblioteca 'class-transformer' ya se sugiere en otra respuesta anterior stackoverflow.com/a/40042282/52277
Michael Freidgeim
-1

Estoy usando Angular 6 en el frontend y la aplicación Spring Boot en el backend que devuelve Java Objects. Todo lo que necesito hacer es definir una clase similar en la aplicación angular que tenga propiedades coincidentes y luego puedo aceptar el objeto 'como' un objeto de clase angular ( comp como Empresa en el ejemplo a continuación).

Consulte el código de front-end a continuación, por ejemplo. Déjame saber en los comentarios si algo necesita más claridad.

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

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

donde empresa es un objeto de entidad en la aplicación de arranque de primavera y también es una clase en Angular.

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