Olá Habr! Quero compartilhar com você minha biblioteca para desserializar objetos JSON em classes, que também valida automaticamente os dados de entrada por tipo.
Não faz muito tempo, as classes apareceram em JavaScript, o que simplificou bastante o processo de escrever código. Infelizmente, porém, a funcionalidade para desserializar JSON para essas mesmas classes não apareceu, ou seja, Você pode serializar uma classe em uma string, mas de volta por conta própria. E para corrigir essa desvantagem, a biblioteca
ts-serializable foi escrita e eu quero compartilhar com você.
A essência do problema é mostrada pelo seguinte código:
export class User { public firstName: string = ""; public lastName: string = ""; public birthDate: Date = new Date(); public getFullName(): string { return [this.firstName, this.lastName].join(' '); } public getAge(): number { return new Date().getFullYear() - this.birthDate.getFullYear(); } } const ivan = new User(); ivan.getFullName();
Qual é a causa dos erros do novo Ivan? O fato é que o método JSON.parse não desserializa para a classe User, mas para a classe Object, que simplesmente não possui os métodos getFullName e getAge.
Como minha biblioteca ajuda a resolver esse problema e desserializa-lo em Usuário, em vez de Objeto? Basta modificar apenas ligeiramente o código:
import { jsonProperty, Serializable } from "ts-serializable"; export class User extends Serializable { @jsonProperty(String) public firstName: string = ""; @jsonProperty(String) public lastName: string = ""; @jsonProperty(Date) public birthDate: Date = new Date(); public getFullName(): string { return [this.firstName, this.lastName].join(' '); } public getAge(): number { return new Date().getFullYear() - this.birthDate.getFullYear(); } } const ivan = new User(); ivan.getFullName(); // ivan.getAge(); // ivan instanceof User; // const text = JSON.stringify(ivan); // const newIvan = new User().fromJson(JSON.parse(text)); // User newIvan.getFullName(); // newIvan.getAge(); // newIvan instanceof User; //
Tudo é muito simples. Herdamos nossa classe da classe Serializable, que possui dois métodos fromJson para desserialização e toJSON para serialização, e desligamos o decorador @jsonProperty com as propriedades que têm permissão para aceitar dados do JSON. Dados inválidos serão ignorados, um aviso será emitido para o console e o valor padrão permanecerá na propriedade.
É basicamente isso. Agora, na frente, você pode desserializar e serializar tão facilmente quanto em C #, Java e outras linguagens. Com base no comportamento do Newtonsoft Json.NET.
Perguntas frequentes
Por que herdar do Serializable?Para adicionar dois métodos fromJson e toJSON ao modelo. Você pode fazer o mesmo através do decorador ou aplicação de patches de macaco. Mas a herança é um método mais adequado para o Typecript.
Como ocorre a validação de dadosNo decorador, você deve designar um construtor desses tipos de dados que podem ser aceitos no JSON. Objetos booleanos, String, Number retornarão booleanos, string, número, respectivamente. Se você precisar aceitar uma matriz, o tipo será enquadrado pelos colchetes da matriz, por exemplo @jsonProperty ([String]). Se o construtor for herdado da classe Serializable, ele também será desserializado para a classe; caso contrário, o objeto será retornado.
Como capturar erros de validação?Por padrão, a biblioteca simplesmente grava avisos no console sobre erros de validação. Para substituir esse comportamento, como lançar exceções ou fazer logon no back-end, você deve substituir o método onWrongType do modelo.
Bônus 1. Cópia detalhada.
const user1 = new Uesr(); const user2 = new User().fromJson(user1);
Bônus 2. ViewModels preguiçosos.
Se você precisar criar um modelo com dados adicionais, por exemplo, para uma visualização, mas que o back-end não aceite, basta expandir o modelo com novas propriedades e marcar essas propriedades com o decorador @jsonIgnore. E então essas propriedades não serão serializadas.
import { jsonProperty, Serializable } from "ts-serializable"; export class User extends Serializable { @jsonProperty(String) public firstName: string = ""; @jsonIgnore() public isExpanded: boolean = false; } JSON.stringify(new User());