Главная » Хабрахабр » [Из песочницы] TypeScript: Десериализация JSON в классы с валидацией типов у свойств

[Из песочницы] TypeScript: Десериализация JSON в классы с валидацией типов у свойств

Привет, Хабр! Хочу поделиться с вами своей библиотекой для десериализации объектов JSON в классы, которая еще и автоматически валидирует по типам входные данные.

Но к сожалению не появился функционал для десериализации JSON в эти самые классы, т.е. Не так давно в JavaScript появилась такая замечательная вещь как классы, которая значительно упростила процесс написания кода. И вот для исправления этого недостатка и была написана библиотека ts-serializable которой я хочу поделиться с вами.
В чем суть проблемы показывает следующий код: сериализовать класс в строку можно, а вот обратно уже своими силами.

export class User 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 = JSON.parse(text); // десериализуем обратно newIvan.getFullName(); // ОшибкаТипа: метод getFullName отсутствует
newIvan.getAge(); // ОшибкаТипа: метод getAge отсутствует
newIvan instanceof User; // не является инстансом пользователя

В чем причина ошибок нового Ивана? Дело в том что метод JSON.parse десериализует не в класс User, а в класс Object у которого просто нету методов getFullName и getAge.

Достаточно просто слегка модифицировать код: Как же моя библиотека помогает решить эту проблему и десериализовать в User, а не Object?

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; // является инстансом пользователя

Все очень просто. Наследуем наш класс от класса Serializable у которого есть два метода fromJson для десериализации и toJSON для сериализации, а свойствам вешаем декоратор @jsonProperty с указанием тех типов данных которые разрешено принимать из JSON. Невалидные данные будут проигнорированы, в консоль выдано предупреждение, а в свойстве останется значение по умолчанию.

Теперь на фронте можно десериализовать и сериализовать так же просто как это делается в C#, Java и других языках. Вот вообщем то и все. NET. За основу взято поведение Newtonsoft Json.

FAQ

Для чего наследоваться от Serializable?

Можно тоже самое реализовать и через декоратор или monkey patching. Для того что бы добавить в модель два метода fromJson и toJSON. Но наследование является более правильным методом для Typescript.

Как происходит валидация данных

Объекты Boolean, String, Number выдадут соответственно boolean, string, number. В декоратор необходимо назначить конструктор тех типов данных которые разрешено принимать из JSON. Если конструктор отнаследован от класса Serializable, то оно также будет десериализовано в класс, если нет то вернется объект. Если вам нужно принять массив, то тип обрамляется скобочками массива, например @jsonProperty([String]).

Как отловить ошибки валидации?

Для переопределения этого поведения, например для бросания исключений или логирования на бекенд, необходимо переопределить метод onWrongType у модели. По умолчанию библиотеку просто пишет предупреждения в консоль об ошибках валидации.

Бонус 1. Глубокая копия.

const user1 = new Uesr();
const user2 = new User().fromJson(user1); // создаст глубокую копию

Бонус 2. Ленивые ViewModels.

Если вам необходимо создать модель с дополнительными данными, например для вьюхи, но которые не принимает бекенд, можно просто расширить модель новыми свойствами и пометить эти свойства декоратором @jsonIgnore. И тогда эти свойства не будут сериализованы.

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()); // вернет {"firstName":"Иван"}


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Компания Sikorsky провела демонстрацию беспилотного вертолёта с человеком на борту

Вертолёт SARA (Sikorsky Autonomy Research Aircraft) на базе Sikorsky S-76 с автопилотом Matrix Technology Созданный набор систем Matrix Technology достиг уже такого уровня, что в течение года компания планирует интегрировать некоторые функции в вертолёты Black Hawk, которые поставляет для армии. ...

[Перевод] Ремастеринг «Звёздного пути» нейросетями до 1080p и 4K

В качестве небольшого любительского проекта я поэкспериментировал с нейросетями AI Gigapixel для апскейла одного из моих любимых научно-фантастических сериалов — Star Trek: Deep Space Nine (DS9), в русском переводе «Звёздный путь: Глубокий космос 9». Так же, как Final Fantasy 7, ...