TypeScript: Deserializing JSON into classes with property type validation

 3r3179. 3r3-31. Hi, Habr! I want to share with you my library for deserializing JSON objects into classes, which also automatically validates input data by type.
 3r3179.
 3r3179. Not so long ago in jаvascript there was such a wonderful thing as classes, which greatly simplified the process of writing code. But unfortunately, there is no functionality for deserializing JSON into these same classes, i.e. You can serialize a class into a string, but you can do it yourself. And to correct this deficiency, the library was written. ts-serializable which I want to share with you.
 3r3179.
3r311.
 3r3179. The essence of the problem is shown by the following code:
 3r3179.
 3r3179.
export class User {3r3179. public firstName: string = "Ivan"; 3r3179. public lastName: string = "Petrov"; 3r3179. public birthDate: Date = new Date (); 3r3179. 3r3179. public getFullName (): string {
return[this.firstName, this.lastName].join (""); 3r3179.}
3r3179. public getAge (): number {
return new Date (). getFullYear () - this.birthDate.getFullYear (); 3r3179.}
}
3r3179. const ivan = new User (); 3r3179. ivan.getFullName (); //returns the full name
ivan.getAge (); //returns age
ivan instanceof User; //is the user instance of
3r3179. const text = JSON.stringify (ivan); //serialize the class to text
const newIvan = JSON.parse (text); //we deserialize back
3r3179. newIvan.getFullName (); //ErrorType: the getFullName method is missing
newIvan.getAge (); //ErrorType: the getAge method is missing
newIvan instanceof User; //is not a user instance
3r3167. 3r3168.
 3r3179. What is the reason for the mistakes of the new Ivan? The fact is that the JSON.parse method deserializes not into the User class, but into the Object class, which simply does not have the getFullName and getAge methods.
 3r3179.
 3r3179. How does my library help solve this problem and deserialize into User, not Object? It is enough just to slightly modify the code:
 3r3179.
 3r3179.
import {jsonProperty, Serializable} from "ts-serializable"; 3r3179. 3r3179. export class User extends Serializable {
@jsonProperty (String)
public firstName: string = "Ivan"; 3r3179. @jsonProperty (String)
public lastName: string = "Petrov"; 3r3179. @jsonProperty (Date)
public birthDate: Date = new Date (); 3r3179. 3r3179. public getFullName (): string {
return[this.firstName, this.lastName].join (""); 3r3179.}
3r3179. public getAge (): number {
return new Date (). getFullYear () - this.birthDate.getFullYear (); 3r3179.}
}
3r3179. const ivan = new User (); 3r3179. ivan.getFullName (); //returns the full name
ivan.getAge (); //returns age
ivan instanceof User; //is the user instance of
3r3179. const text = JSON.stringify (ivan); //serialize the class to text
const newIvan = new User (). fromJson (JSON.parse (text)); //we deserialize back to User
3r3179. newIvan.getFullName (); //returns the full name
newIvan.getAge (); //returns age
newIvan instanceof User; //is the user instance of
3r3167. 3r3168.
 3r3179. Everything is very simple. We inherit our class from the Serializable class, which has two fromJson methods for deserialization and toJSON for serialization, and the properties are hung by the @jsonProperty decorator with an indication of the data types that can be accepted from JSON. Empty data will be ignored, a warning will be issued to the console, and the default value will remain in the property.
 3r3179.
 3r3179. That's all that's all. Now on the front, you can deserialize and serialize as easily as it does in C #, Java, and other languages. The basis for the behavior of Newtonsoft Json.NET.
 3r3179.
 3r3179. 3r3147. FAQ
 3r3179. Why inherit from Serializable?
 3r3179.
 3r3179. In order to add two methods fromJson and toJSON to the model. You can do the same thing through a decorator or monkey patching. But inheritance is a better method for typescript.
 3r3179.
 3r3179. How does data validation
 3r3179.
 3r3179. In the decorator, you must assign the constructor of the data types that are allowed to receive from JSON. Objects Boolean, String, Number will be given, respectively, boolean, string, number. If you need to accept an array, then the type is framed by array brackets, for example @jsonProperty ([String]). If the constructor is inherited from the Serializable class, it will also be deserialized into the class, if not, the object will be returned.
 3r3179.
 3r3179. How to catch validation errors?
 3r3179.
 3r3179. By default, the library simply writes warnings to the validation error console. To override this behavior, for example, to throw exceptions or logging to the backend, you need to override the model's onWrongType method.
 3r3179.
 3r3179. 3r3147. Bonus 1. Deep copy. 3r3148.
 3r3179.
const user1 = new Uesr (); 3r3179. const user2 = new User (). fromJson (user1); //create a deep copy of
3r3167. 3r3168.
 3r3179. 3r3147. Bonus 2. Lazy ViewModels. 3r3148.
 3r3179. If you need to create a model with additional data, for example, for a view, but which does not accept the backend, you can simply expand the model with new properties and mark these properties with the @jsonIgnore decorator. And then these properties will not be serialized.
 3r3179.
 3r3179.
import {jsonProperty, Serializable} from "ts-serializable"; 3r3179. 3r3179. export class User extends Serializable {
@jsonProperty (String)
public firstName: string = "Ivan"; 3r3179. @jsonIgnore ()
public isExpanded: boolean = false; 3r3179.}
3r3179. JSON.stringify (new User ()); //returns {"firstName": "Ivan"}
3r3167. 3r3168. 3r33175. 3r3179. 3r3179. 3r3172. ! function (e) {function t (t, n) {if (! (n in e)) {for (var r, a = e.document, i = a.scripts, o = i.length; o-- ;) if (-1! == i[o].src.indexOf (t)) {r = i[o]; break} if (! r) {r = a.createElement ("script"), r.type = "text /jаvascript", r.async =! ? r.defer =! ? r.src = t, r.charset = "UTF-8"; var d = function () {var e = a.getElementsByTagName ("script")[0]; e.parentNode.insertBefore (r, e)}; "[object Opera]" == e.opera? a.addEventListener? a.addEventListener ("DOMContentLoaded", d,! 1): e.attachEvent ("onload", d ): d ()}}} t ("//mediator.mail.ru/script/2820404/"""_mediator") () (); 3r3173. 3r3179. 3r33175. 3r3179. 3r3179. 3r3179. 3r3179.
+ 0 -

Add comment