Como converter um objeto JSON em uma classe de texto datilografado

393

Eu li um objeto JSON de um servidor REST remoto. Este objeto JSON possui todas as propriedades de uma classe de texto datilografada (por design). Como converter esse objeto JSON recebido em um tipo var?

Não quero preencher um var datilografado (ou seja, ter um construtor que aceite esse objeto JSON). É grande e copiar tudo através de subobjeto por subobjeto e propriedade por propriedade levaria muito tempo.

Atualização: No entanto, você pode convertê-lo em uma interface de texto datilografado!

David Thielen
fonte
você pode usar github.com/vojtechhabarta/typescript-generator para gerar interfaces de texto datilografado no caso do seu JSON é mapeada usando classes Java
Vojta
Eu codificado uma pequena biblioteca seleção de elenco: sulphur-blog.azurewebsites.net/typescript-mini-cast-library
Camille Wintz
11
Objeto JSON = Objeto JavaScript Object Notation. Não tem objetos suficientes lá, eu digo que jogamos mais alguns por uma boa medida.
bug-a-lot
11
Eu criei
beshanoe
Criar um protótipo da classe TypeScript para definir seu objeto não prejudicará o código de produção real. Dê uma olhada no arquivo JS compilado, todas as definições serão removidas, pois não fazem parte do JS.
FisNaN

Respostas:

167

Você não pode simplesmente converter um resultado JavaScript simples e antigo de uma solicitação Ajax em uma instância de classe JavaScript / TypeScript prototípica. Existem várias técnicas para fazer isso e geralmente envolvem a cópia de dados. A menos que você crie uma instância da classe, ela não terá métodos ou propriedades. Ele permanecerá um objeto JavaScript simples.

Embora se você estivesse lidando apenas com dados, poderia fazer uma conversão para uma interface (por ser puramente uma estrutura de tempo de compilação), isso exigiria o uso de uma classe TypeScript que usa a instância de dados e executa operações com esses dados.

Alguns exemplos de cópia dos dados:

  1. Copiando o objeto AJAX JSON para o objeto existente
  2. Analisar String JSON em um Protótipo de Objeto Particular em JavaScript

Em essência, você apenas:

var d = new MyRichObject();
d.copyInto(jsonResult);
WiredPrairie
fonte
Eu concordo com a sua resposta. Além disso, embora eu não esteja em um lugar para procurá-lo e testá-lo agora, acho que esses dois passos podem ser combinados, dando uma função de ativação como um parâmetro para JSON.parse(). Ambos ainda precisariam ser feitos, mas sintaticamente poderiam ser combinados.
JAAulde
Claro, isso pode funcionar também - não tenho a sensação de que seria mais eficiente, pois seria necessário chamar uma chamada de função extra para cada propriedade.
WiredPrairie
Definitivamente não é a resposta que eu estava procurando :( Por curiosidade, por que isso? Parece-me como o javascript funciona que isso deve ser possível.
David Thielen
Não, ele não funciona no TypeScript porque não existe uma maneira simples no JavaScript de fazer isso.
WiredPrairie
11
A respeito Object.setPrototypeOf
Petah
102

Eu tive o mesmo problema e encontrei uma biblioteca que faz o trabalho: https://github.com/pleerock/class-transformer .

Funciona assim:

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

Ele suporta crianças aninhadas, mas você precisa decorar o membro da sua turma.

Pak
fonte
14
Esta pequena e brilhante biblioteca resolveu-a perfeitamente com o mínimo de esforço ( @Typeembora não esqueça suas anotações). Esta resposta merece mais crédito.
Benny Bottema
Oh wow !, esta biblioteca não é tão pequeno que tem talvez tudo que você precisa, mesmo permite controlar a transformação com o decorador @transform: D
Diego Fernando Murillo Valenci
3
Observe que esta biblioteca mal é mais mantida. Ele não funciona mais com o Angular5 + e, como eles nem estão mais mesclando solicitações pull, acho que não vão trabalhar nisso tão cedo. É uma ótima biblioteca embora.
precisa saber é
Existe uma solução alternativa para o Angular5 + (na verdade, é um bug angular): github.com/typestack/class-transformer/issues/108
Pak
2
Isso funciona muito bem no Angular 6 (pelo menos para o meu caso de uso, que é apenas o mapeamento literal da classe JSON <=>)
tftd
54

No TypeScript, você pode fazer uma asserção de tipo usando uma interface e genéricos como:

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

Onde ILocationMap descreve a forma dos seus dados. A vantagem desse método é que seu JSON pode conter mais propriedades, mas a forma satisfaz as condições da interface.

Espero que ajude!

user756310
fonte
49
FYI: É uma afirmação de tipo, não um elenco.
WiredPrairie
6
Veja aqui a diferença entre uma asserção de tipo e uma conversão .
Stefan Hanke
7
Onde posso encontrar o Utilities.JSONLoader?
HypeXR
22
Mas não terá métodos, como mencionado na resposta.
Martín Coll
11
@StefanHanke parece que o URL mudou um pouco: "Asserção de Tipo vs. Fundição"
ruffin
37

Se você estiver usando o ES6, tente o seguinte:

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

Mas, dessa maneira , não funcionará no objeto ninho , infelizmente.

migcoder
fonte
4
Eles pediram isso no texto datilografado.
22417 joh.feser
Olá @ joe.feser, mencionei o ES6 porque desta forma o método 'Object.assign' é necessário.
Migcoder
11
Caso o construtor padrão esteja ausente, a instância de destino pode ser criada Object.create(MyClass.prototype), ignorando completamente o construtor.
Marcello
Mais explicações sobre a limitação de objetos aninhados, consulte stackoverflow.com/questions/22885995/…
Michael Freidgeim
28

Encontrei um artigo muito interessante sobre a conversão genérica de JSON para uma classe Typescript:

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

Você acaba com o seguinte 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
fonte
20

TLDR: um revestimento

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

A resposta detalhada

Eu não recomendaria a abordagem Object.assign, pois ela pode sobrecarregar inadequadamente sua instância de classe com propriedades irrelevantes (bem como fechamentos definidos) que não foram declarados na própria classe.

Na classe em que você está tentando desserializar, eu garantiria que todas as propriedades que você deseja desserializar sejam definidas (nulo, matriz vazia, etc.). Ao definir suas propriedades com valores iniciais, você expõe sua visibilidade ao tentar iterar os membros da classe para os quais atribuir valores (consulte desserializar o método abaixo).

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

No exemplo acima, eu simplesmente criei um método de desserialização. Em um exemplo do mundo real, eu o centralizaria em uma classe base ou método de serviço reutilizável.

Aqui está como utilizar isso em algo como um http resp ...

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

Se tslint / ide reclamar que o tipo de argumento é incompatível, basta converter o argumento no mesmo tipo usando colchetes angulares <YourClassName>, exemplo:

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

Se você possui membros de classe de um tipo específico (aka: instância de outra classe), pode convertê-los em instâncias digitadas por meio 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));
  }
}

O exemplo acima desserializa o acct e a lista de tarefas em suas respectivas instâncias de classe.

Timothy Perez
fonte
Recebo esta mensagem de erro: Digite '{name: string, age: number, id: number}' não pode ser convertido para o tipo 'Person'. A propriedade 'id' é particular no tipo 'Pessoa', mas não no tipo '{nome: sequência, idade: número, id: número}'
#
Como devo usar isso com enumerações? Preciso usar a abordagem de tipo específico e adicionar getter e setter para ela?
Tadija Bagarić
@TimothyParez Quando você define as tarefas?
Kay
Eu tentei fazer algo semelhante, mas minha matriz de tarefas está vazia quando eu console.log pessoa.
Kay
Para que isso fosse compilado, tive que adicionar uma Assinatura de Índice à classe: export class Pessoa {[key: string]: any (...)}
Asimov
18

Supondo que o json tenha as mesmas propriedades da sua classe de texto datilografado, você não precisará copiar suas propriedades Json para o seu objeto datilografado. Você apenas precisará construir seu objeto TypeScript passando os dados json no construtor.

No seu retorno de chamada ajax, você recebe uma empresa:

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

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

Para fazer esse trabalho:

1) Adicione um construtor em sua classe Typescript que tome os dados json como parâmetro. Nesse construtor você estender o seu objeto JSON com jQuery, como este: $.extend( this, jsonData). $ .extend permite manter os protótipos javascript enquanto adiciona as propriedades do objeto json.

2) Observe que você terá que fazer o mesmo para objetos vinculados. No caso de Funcionários no exemplo, você também cria um construtor que toma a parte dos dados json dos funcionários. Você chama $ .map para converter funcionários json em objetos de texto datilografados de Funcionário.

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 é a melhor solução que encontrei ao lidar com classes Typescript e objetos json.

Anthony Brenelière
fonte
Eu prefiro essa solução do que implementar e manter interfaces, porque meus aplicativos Angular2 têm um modelo de aplicativo real que pode ser diferente do modelo dos serviços da Web que meu aplicativo consome. Pode ter dados particulares e propriedades calculadas.
Anthony Brenelière
7
Usar o JQuery em projetos angulares é uma péssima idéia. E se seus modelos contiverem várias funções, eles não serão mais modelos.
Davor
11
@Davor Você quer dizer POJO, ou modelo? POJO (basicamente objetos simples) não tem funções, enquanto modelo é um termo mais amplo e inclui repositório. O padrão do repositório, em contraste com o POJO, é sobre funções, mas ainda é modelo.
forsberg
@ Davor: usar JQuery em projetos angulares não é uma má idéia, desde que você não o utilize para manipular o DOM, o que é realmente uma péssima idéia. Uso qualquer biblioteca necessária para meus projetos Angular e, para o jQuery, não é uma opção porque meu projeto usa o SignalR que depende dele. No caso de classes, agora usadas pelo javascript ES6, os dados são acessados ​​com propriedades que são funções que encapsulam a maneira como os dados são armazenados na memória. Para os construtores, existe uma maneira adequada de usar fábricas.
Anthony Brenelière
O OP é claramente sobre modelos de dados simples para REST. Você está tornando isso desnecessariamente complicado, pessoal. E sim, você pode usar o Jquery para outras coisas, mas está importando uma biblioteca enorme para usar 1% dela. Isso é um cheiro de código, se eu já vi um.
Davor
18

Ainda não há nada para verificar automaticamente se o objeto JSON que você recebeu do servidor possui as propriedades de interface do datilógrafo esperadas (a leitura está em conformidade com as). Mas você pode usar proteções de tipo definido pelo usuário

Considerando a seguinte interface e um objeto json bobo (poderia ter sido de qualquer tipo):

interface MyInterface {
    key: string;
 }

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

Três maneiras possíveis:

A. Asserção de tipo ou conversão estática simples colocada após a variável

const myObject: MyInterface = json as MyInterface;

B. Molde estático simples, antes da variável e entre diamantes

const myObject: MyInterface = <MyInterface>json;

C. Elenco dinâmico avançado, você verifica a estrutura do 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}'.`);
}

Você pode brincar com este exemplo aqui

Observe que a dificuldade aqui é escrever a isMyInterfacefunção. Espero que o TS adicione um decorador, mais cedo ou mais tarde, para exportar a digitação complexa para o tempo de execução e deixe o tempo de execução verificar a estrutura do objeto, quando necessário. Por enquanto, você pode usar um validador de esquema json cujo objetivo é aproximadamente o mesmo OU esse gerador de função de verificação de tipo de tempo de execução

Flavien Volken
fonte
11
Sua resposta deve estar no topo, eu acho
afzalex
Definitivamente. Esta é a melhor resposta que se baseia diretamente no site ts.
Burak Karakuş 12/03
15

No meu caso, funciona. Eu usei as funções Object.assign (destino, fontes ...) . Primeiro, a criação do objeto correto e, em seguida, copia os dados do objeto json para o destino.

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

E um exemplo mais avançado de uso. Um exemplo usando a 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
fonte
O que acontece quando você tem uma propriedade privada?
prasanthv
Porque o objeto jsonUser não é uma classe de usuário. Sem operação Object.assign (u, jsonUsers); Você não pode usar a função getId (). Somente após a atribuição, você obtém um objeto Usuário válido no qual você pode usar a função getId (). A função getId () é apenas para o exemplo de que a operação foi bem-sucedida.
precisa
você pode ignorar a temperatura var - basta fazerthis.users[i] = new User(); Object.assign(this.users[i], users[i])
cyptus
ou melhor ainda fazer uso do valor de retorno:this.users[i] = Object.assign(new User(), users[i]);
cyptus
Esta versão longa é apenas uma explicação. Você pode encurtar o código, tanto quanto você gosta :)
Adam111p
6

Enquanto não estiver lançando por si só; Eu achei https://github.com/JohnWhiteTB/TypedJSON uma 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
fonte
11
Não está lançando, o que realmente faz?
21717 DanielM
11
Esta solução requer muitas anotações. Não existe realmente uma maneira mais fácil?
JPNotADragon 30/09/19
3

Você pode criar um interfacedo seu tipo ( SomeType) e converter o objeto nisso.

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

Se você precisar converter seu objeto json para uma classe de texto datilografado e ter seus métodos de instância disponíveis no objeto resultante que você precisa usar Object.setPrototypeOf, como eu fiz no trecho de código abaixo:

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

Uma pergunta antiga com respostas principalmente corretas, mas não muito eficientes. Isto é o que eu proponho:

Crie uma classe base que contenha o método init () e métodos de conversão estática (para um único objeto e uma matriz). Os métodos estáticos podem estar em qualquer lugar; a versão com a classe base e init () permite extensões fáceis depois.

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ânica semelhante (com assign () ) foi mencionada na publicação do @ Adam111p. Apenas outra maneira (mais completa) de fazer isso. @ Timothy Perez é crítico em relação a assign () , mas é totalmente apropriado aqui.

Implemente uma classe derivada (a 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);
        }
    }
}

Agora podemos converter um objeto recuperado do serviço:

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

Toda a hierarquia de objetos SubjectArea terá a classe correta.

Um caso de uso / exemplo; crie um serviço Angular (classe base abstrata novamente):

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

O uso se torna muito simples; crie um serviço de área:

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

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

pegue() método do serviço retornará uma promessa de uma matriz já lançada como objetos SubjectArea (hierarquia inteira)

Agora digamos, temos outra classe:

export class OtherItem extends ContentItem {...}

Criar um serviço que recupera dados e lança para a classe correta é tão simples quanto:

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

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

Use uma classe estendida de uma interface.

Então:

Object.assign(
                new ToWhat(),
                what
              )

E melhor:

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

O que se torna um controlador do DataInterface.

Sam
fonte
1

https://jvilk.com/MakeTypes/

você pode usar este site para gerar um proxy para você. ele gera uma classe e pode analisar e validar seu objeto JSON de entrada.

Amir Mehrabi
fonte
0

Nos últimos anos, você pode fazer o seguinte:

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

E que usuário como este:

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

Encontrei uma necessidade semelhante. Eu queria algo que me proporcionasse uma fácil transformação de / para JSON proveniente de uma chamada API REST de / para uma definição de classe específica. As soluções que encontrei eram insuficientes ou pretendiam reescrever o código das minhas aulas e adicionar anotações ou similares.

Eu queria que algo como o GSON fosse usado em Java para serializar / desserializar classes de / para objetos JSON.

Combinado com uma necessidade posterior, de que o conversor funcione também em JS, acabei escrevendo meu próprio pacote.

No entanto, tem um pouco de sobrecarga. Mas quando iniciado, é muito conveniente adicionar e editar.

Você inicializa o módulo com:

  1. esquema de conversão - permitindo mapear entre campos e determinar como a conversão será feita
  2. Matriz de mapa de classes
  3. Mapa de funções de conversão - para conversões especiais.

Então, no seu código, você usa o módulo inicializado como:

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

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

ou, para JSON:

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

Use este link para o pacote npm e também uma explicação detalhada de como trabalhar com o módulo: json-class-converter

Também o envolveu para
uso Angular em: angular-json-class-converter

Doronm
fonte
0

Passe o objeto como está para o construtor da classe; Sem convenções ou verificações

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

Você pode usar este pacote npm. https://www.npmjs.com/package/class-converter

É fácil de usar, por exemplo:

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-chita
fonte
0

Pessoalmente, acho espantoso que o texto datilografado não permita que uma definição de terminal especifique o tipo do objeto que está sendo recebido. Como parece que esse é realmente o caso, eu faria o que fiz com outras linguagens, separando o objeto JSON da definição de classe e fazendo com que a definição de classe usasse o objeto JSON como seu único membro de dados .

Eu desprezo o código padrão, portanto, para mim, geralmente é uma questão de obter o resultado desejado com a menor quantidade de código enquanto preservo o tipo.

Considere as seguintes definições de estrutura de objeto JSON - elas seriam o que você receberia em um nó de extremidade; elas são apenas definições de estrutura, sem métodos.

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

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

Se pensarmos no exposto em termos orientados a objetos, as interfaces acima não são classes porque definem apenas uma estrutura de dados. Uma classe em termos OO define dados e o código que opera neles.

Agora, definimos uma classe que especifica os dados e o código que opera neles ...

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 agora podemos simplesmente passar qualquer objeto em conformidade com a estrutura IPerson e seguir nosso caminho ...

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

Da mesma forma, agora podemos processar o objeto recebido no seu endpoint com algo ao longo das linhas de ...

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

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

Isso tem muito mais desempenho e usa metade da memória de cópia dos dados, enquanto reduz significativamente a quantidade de código padrão que você deve escrever para cada tipo de entidade. Simplesmente conta com a segurança de tipo fornecida pelo TypeScript.

Rodney P. Barbati
fonte
0

Use a declaração 'as':

const data = JSON.parse(response.data) as MyClass;
Daniel Valdebenito
fonte
Essa técnica foi mencionada nesta resposta de dois anos antes e, como foi discutido em outros lugares, não adiciona nenhuma função que possa ser declarada MyClass.
Heretic Monkey
-1

Esta é uma opção simples e realmente boa

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

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

E então você terá

objectConverted.name
SamazoOo
fonte
-1

Usei esta biblioteca aqui: https://github.com/pleerock/class-transformer

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

Implementação:

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

Às vezes, você precisará analisar os valores JSON para plainToClass para entender que são dados formatados em JSON

Mac Chibueze
fonte
A biblioteca 'transformador de classe' já é sugerida em outra resposta acima stackoverflow.com/a/40042282/52277
Michael Freidgeim
-1

Estou usando o Angular 6 no frontend e o aplicativo Spring Boot no backend, que retorna objetos Java. Tudo o que preciso fazer é definir uma classe semelhante no aplicativo angular que tenha propriedades correspondentes e então eu posso aceitar o objeto 'como' um objeto de classe angular ( comp como Company no exemplo abaixo).

Consulte o código de front-end abaixo, por exemplo. Deixe-me saber nos comentários se algo precisa de mais clareza.

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

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

em que empresa é um objeto de entidade no aplicativo de inicialização por primavera e também é uma classe em Angular.

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