Hola Habr! Quiero compartir con ustedes mi biblioteca para deserializar objetos JSON en clases, que también valida automáticamente los datos de entrada por tipo.
No hace mucho tiempo, algo tan maravilloso como las clases aparecieron en JavaScript, lo que simplificó enormemente el proceso de escribir código. Pero desafortunadamente, la funcionalidad para deserializar JSON en estas mismas clases no apareció, es decir Puede serializar una clase en una cadena, pero de nuevo por su cuenta. Y para solucionar este inconveniente, se escribió la biblioteca
ts-serializable que quiero compartir con ustedes.
La esencia del problema se muestra en el siguiente 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();
¿Cuál es la causa de los errores del nuevo Ivan? El hecho es que el método JSON.parse no se deserializa a la clase User, sino a la clase Object, que simplemente no tiene los métodos getFullName y getAge.
¿Cómo ayuda mi biblioteca a resolver este problema y deserializarlo en Usuario en lugar de Objeto? Es suficiente modificar ligeramente el 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; //
Todo es muy sencillo. Heredamos nuestra clase de la clase Serializable, que tiene dos métodos fromJson para deserialización y toJSON para serialización, y colgamos el decorador @jsonProperty con las propiedades que pueden aceptar datos de JSON. Se ignorarán los datos no válidos, se emitirá una advertencia a la consola y el valor predeterminado permanecerá en la propiedad.
Eso es básicamente todo. Ahora en el frente, puede deserializar y serializar tan fácilmente como lo hace en C #, Java y otros lenguajes. Basado en el comportamiento de Newtonsoft Json.NET.
FAQ
¿Por qué heredar de serializable?Para agregar dos métodos fromJson y toJSON al modelo. Puede hacer lo mismo a través de decorador o parches de mono. Pero la herencia es un método más apropiado para Typecript.
Cómo se produce la validación de datosEn el decorador, debe asignar un constructor de esos tipos de datos que pueden ser aceptados por JSON. Boolean, String, Number objetos devolverán boolean, string, number, respectivamente. Si necesita aceptar una matriz, entonces el tipo está enmarcado por los corchetes de la matriz, por ejemplo @jsonProperty ([String]). Si el constructor se hereda de la clase Serializable, también se deserializará en la clase; de lo contrario, se devolverá el objeto.
¿Cómo atrapar errores de validación?Por defecto, la biblioteca simplemente escribe advertencias en la consola sobre errores de validación. Para anular este comportamiento, como lanzar excepciones o iniciar sesión en el back-end, debe anular el método onWrongType del modelo.
Bonificación 1. Copia profunda.
const user1 = new Uesr(); const user2 = new User().fromJson(user1);
Bonificación 2. Lazy ViewModels.
Si necesita crear un modelo con datos adicionales, por ejemplo, para vistas, pero que el backend no acepta, simplemente puede expandir el modelo con nuevas propiedades y marcar estas propiedades con el decorador @jsonIgnore. Y luego estas propiedades no se serializarán.
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());