Eu recebo um objeto JSON de uma chamada AJAX para um servidor REST. Este objeto possui nomes de propriedades que correspondem à minha classe TypeScript (este é o seguimento desta pergunta ).
Qual é a melhor maneira de inicializá-lo? Eu não acho que isso funcionará porque a classe (objeto & JSON) possui membros que são listas de objetos e membros que são classes, e essas classes têm membros que são listas e / ou classes.
Mas eu prefiro uma abordagem que procure os nomes dos membros e os atribua, criando listas e instanciando classes conforme necessário, para que eu não precise escrever código explícito para cada membro de cada classe (há MUITO!)
json
typescript
David Thielen
fonte
fonte
Respostas:
Estas são algumas fotos rápidas para mostrar algumas maneiras diferentes. Eles não são de forma alguma "completos" e, como um aviso, não acho que seja uma boa ideia fazê-lo dessa maneira. Além disso, o código não está muito limpo, pois eu o digitei rapidamente.
Também como observação: é claro que as classes desserializáveis precisam ter construtores padrão, como é o caso em todos os outros idiomas em que tenho conhecimento de qualquer tipo de desserialização. Obviamente, o Javascript não reclamará se você chamar um construtor não padrão sem argumentos, mas é melhor que a classe esteja preparada para isso (além disso, não seria realmente a "maneira de escrever um texto").
Opção 1: nenhuma informação de tempo de execução
O problema com essa abordagem é principalmente que o nome de qualquer membro deve corresponder à sua classe. O que limita automaticamente a um membro do mesmo tipo por classe e quebra várias regras de boas práticas. Eu recomendo fortemente contra isso, mas apenas liste aqui porque foi o primeiro "rascunho" quando escrevi esta resposta (e é também por isso que os nomes são "Foo" etc.).
Opção # 2: a propriedade name
Para se livrar do problema na opção 1, precisamos ter algum tipo de informação sobre o tipo de nó no objeto JSON. O problema é que, no Typescript, essas coisas são construções em tempo de compilação e precisamos delas em tempo de execução - mas os objetos de tempo de execução simplesmente não têm conhecimento de suas propriedades até serem definidas.
Uma maneira de fazer isso é conscientizando as classes de seus nomes. Você também precisa dessa propriedade no JSON. Na verdade, você só precisa dele no json:
Opção nº 3: declarando explicitamente os tipos de membros
Como mencionado acima, as informações de tipo dos membros da classe não estão disponíveis no tempo de execução - ou seja, a menos que as disponibilizemos. Só precisamos fazer isso para membros não primitivos e estamos prontos para:
Opção 4: A maneira detalhada, mas organizada
Atualização 01/03/2016: Como o @GameAlchemist apontou nos comentários ( idéia , implementação ), a partir do Typescript 1.7, a solução descrita abaixo pode ser escrita de uma maneira melhor usando decoradores de classe / propriedade.
A serialização é sempre um problema e, na minha opinião, a melhor maneira é apenas a mais curta. De todas as opções, é isso que eu prefiro, porque o autor da classe tem controle total sobre o estado dos objetos desserializados. Se eu tivesse que adivinhar, diria que todas as outras opções, mais cedo ou mais tarde, causarão problemas (a menos que o Javascript tenha uma maneira nativa de lidar com isso).
Realmente, o exemplo a seguir não faz justiça à flexibilidade. Realmente apenas copia a estrutura da classe. A diferença que você deve ter em mente aqui, porém, é que a classe tem controle total para usar qualquer tipo de JSON que deseja controlar o estado de toda a classe (você pode calcular as coisas etc.).
fonte
equals
outoString
métodos (apenas que você normalmente os gera automaticamente). Não deve ser muito difícil escrever um gerador,deserialize
se você quiser, mas não pode ser a automação em tempo de execução.você pode usar
Object.assign
Não sei quando isso foi adicionado. No momento, estou usando o Typescript 2.0.2 e esse parece ser um recurso do ES6.aqui está
HalJson
aqui está o que o Chrome diz que é
para que você possa ver que não faz a atribuição recursivamente
fonte
Object.assign
. Por que temos duas respostas semelhantes ao léxico acima desta?Object.assign
não funcionará recursivamente e não instanciar tipos de objetos corretos, deixando valores comoObject
instâncias. Embora seja adequado para tarefas triviais, a serialização de tipo complexo não é possível com ele. Por exemplo, se uma propriedade de classe for de um tipo de classe customizada,JSON.parse
+Object.assign
instanciará essa propriedade paraObject
. Os efeitos colaterais incluem métodos e acessadores ausentes.Object.assign
, onde ainda se trata de escrever instanciações aninhadas por mão. Essa abordagem é adequada para objetos muito simples no nível de tutorial, mas não para uso real.TLDR: TypedJSON (prova de conceito de trabalho)
A raiz da complexidade desse problema é que precisamos desserializar o JSON em tempo de execução usando informações de tipo que só existem em tempo de compilação . Isso requer que as informações de tipo sejam disponibilizadas de alguma forma no tempo de execução.
Felizmente, isso pode ser resolvido de uma maneira muito elegante e robusta com decoradores e ReflectDecorators :
Informações sobre o tipo de gravação
Com uma combinação de ReflectDecorators e decoradores de propriedades, as informações de tipo podem ser facilmente registradas sobre uma propriedade. Uma implementação rudimentar dessa abordagem seria:
Para qualquer propriedade, o trecho acima adicionará uma referência da função construtora da propriedade à
__propertyTypes__
propriedade oculta no protótipo de classe. Por exemplo:E é isso, temos as informações de tipo necessárias em tempo de execução, que agora podem ser processadas.
Informações de tipo de processamento
Primeiro precisamos obter uma
Object
instância usandoJSON.parse
- depois disso, podemos iterar sobre as entradas em__propertyTypes__
(coletadas acima) e instanciar as propriedades necessárias de acordo. O tipo do objeto raiz deve ser especificado, para que o desserializador tenha um ponto de partida.Novamente, uma implementação simples e simples dessa abordagem seria:
A idéia acima tem uma grande vantagem de desserializar por tipos esperados (para valores complexos / de objetos), em vez do que está presente no JSON. Se a
Person
é esperado, é umaPerson
instância criada. Com algumas medidas de segurança adicionais em vigor para tipos e matrizes primitivos, essa abordagem pode ser protegida, resistindo a qualquer JSON malicioso.Casos Edge
No entanto, se você está feliz agora que a solução é que simples, eu tenho uma má notícia: há um grande número de casos de ponta que precisam ser tomadas de cuidados. Apenas alguns dos quais são:
Se você não quiser mexer com tudo isso (aposto que não), ficaria feliz em recomendar uma versão experimental de trabalho de uma prova de conceito utilizando essa abordagem, o TypedJSON - que eu criei para resolver esse problema exato, um problema que eu enfrento diariamente.
Devido à forma como os decoradores ainda estão sendo considerados experimentais, eu não recomendaria usá-lo para uso em produção, mas até agora isso me serviu bem.
fonte
Eu tenho usado esse cara para fazer o trabalho: https://github.com/weichx/cerialize
É muito simples, mas poderoso. Suporta:
Exemplo:
fonte
Eu criei uma ferramenta que gera interfaces TypeScript e um "mapa de tipos" em tempo de execução para executar a digitação em tempo de execução com os resultados de
JSON.parse
: ts.quicktype.ioPor exemplo, dado esse JSON:
quicktype produz a seguinte interface TypeScript e tipo de mapa:
Em seguida, verificamos o resultado de
JSON.parse
contra o mapa de tipos:Deixei de fora algum código, mas você pode tentar o quicktype para obter os detalhes.
fonte
Opção 5: Usando construtores TypeScript e jQuery.extend
Este parece ser o método mais sustentável: adicione um construtor que tome como parâmetro a estrutura json e estenda o objeto json. Dessa forma, você pode analisar uma estrutura json em todo o modelo de aplicativo.
Não há necessidade de criar interfaces ou listar propriedades no construtor.
No retorno de chamada ajax, você recebe uma empresa para calcular salários:
fonte
$.extend
vem?Object.assign
, o que remove essa dependência do jQuery.Para objetos simples, eu gosto deste método:
Aproveitar a capacidade de definir propriedades no construtor permite que seja conciso.
Isso gera um objeto digitado (em comparação com todas as respostas que usam Object.assign ou alguma variante, que fornece um objeto) e não requer bibliotecas ou decoradores externos.
fonte
A quarta opção descrita acima é uma maneira simples e agradável de fazer isso, que deve ser combinada com a segunda opção no caso em que você precisa lidar com uma hierarquia de classes como, por exemplo, uma lista de membros que é uma das ocorrências de subclasses de uma superclasse de membro, por exemplo, o diretor estende o membro ou o aluno estende o membro. Nesse caso, você deve fornecer o tipo de subclasse no formato json
fonte
O JQuery .extend faz isso para você:
fonte
Talvez não seja uma solução real, mas simples:
trabalhar para dependências difíceis também !!!
fonte
baz
será do tipoObject
e não do tipoBar.
. Funciona neste caso simples porqueBar
não possui métodos (apenas propriedades primitivas). SeBar
tivesse um método parecidoisEnabled()
, essa abordagem falharia, pois esse método não estaria na sequência JSON serializada.Outra opção usando fábricas
https://github.com/MrAntix/ts-deserialize
use assim
fonte
o melhor que encontrei para esse fim é o transformador de classe. github.com/typestack/class-transformer
É assim que você o usa:
Alguma classe:
Se você usar as propriedades aninhadas do decorador @Type, também serão criadas.
fonte
Pessoalmente, prefiro a opção nº 3 do @Ingo Bürk. E aprimorei seus códigos para suportar uma matriz de dados complexos e uma matriz de dados primitivos.
fonte
você pode fazer como abaixo
e
fonte
fonte