Serializing?
Serialization is the process of converting complex objects into a data format that can be easily stored or sent across a network connection. In JavaScript, serialization and deserialization of objects can be achieved by using JSON.stringify()
and JSON.parse()
.
Example
// Let's say you have a simple json response from your server. var jsonResponse = "{firstName:\"John\", lastName:\"Doe\"}"; // If you want to deserialize this object, you can use JSON.parse(). var jsonParsed = JSON.parse(jsonResponse); // An object with the correct properties will be returned console.log(jsonParsed.firstName); // Prints 'John' console.log(jsonParsed.lastName); // Prints 'Doe' // If you want to serialize the object back into a string represantation, you can use JSON.stringify() var serializedJson = JSON.stringify(parsedJson); console.log(serializedJson); // Prints '{firstName:"John", lastName:"Doe"}'
The Problem?
Things start to get complicated when you are using TypeScript
or ES6
and the response from the server doesn’t really match your client data structure. In this case you have an extra step of copying the properties from the parsed json response into your custom model object. When you want to send the data back to the server you have to copy the properties again into their original format.
Example
// The response from the server let response:string = "{first_name:\"John\", last_name:\"Doe\"}"; // Your model class class UserProfile { firstName:string; lastName:string; } let jsonParsed = JSON.parse(response); var userProfile = new UserProfile(); userProfile.firstName = jsonParsed.first_name; userProfile.lastName = jsonParsed.last_name; // When you want to send the data back to the server you have to do the same things as above but in reverse. let dataToSend = { first_name: userProfile.firstName, last_name: userProfile.lastName }; let dataAsString = JSON.stringify(dataToSend); console.log(dataAsString); // Prints {first_name:"John", last_name:"Doe"} // It's easy to see that when you have lots of properties this gets very messy, very quickly
Solution? Introducing TS Serializer
Some time ago I have started using TypeScript for most of my projects. One of those projects had a very different data structure between the server and the client. After getting very frustated with writing serialization and deserialization methods for my data models, I came up with the ideea of ts-serializer. ts-serializer
is a collection of typescript decorators and helper classes that allows the developer to easily serialize and deserialize complex objects to and from JSON objects.
Installation
Using NPM
npm install --save ts-serializer
Build from Git
git clone https://github.com/dpopescu/ts-serializer.git cd ts-serializer npm install npm run build
The library will be in the dist
folder
Usage
You start by importing the library decorators and the Serializable abstract class
import {Serialize, SerializeProperty, Serializable} from 'ts-serializer';
In order to mark a class as serializable, you need to use the Serialize decorator and extend the abstract Serializable class.
@Serialize({}) class Profile extends Serializable { }
The Serialize
decorator implements in the target class the serialize and deserialize methods required by the Serializable
class.
By default, the library does not serialize class properties if are not marked for serialization. In order to declare a property as serializable you use the SerializeProperty decorator.
@Serialize({}) class Profile extends Serializable { @SerializeProperty({}) firstName:string; @SerializeProperty({}) lastName:string; }
After the class and the class properties are marked as serializable, you can use the serialize
and deserialize
methods and ts-serializer
will take care of things for you.
Full Example
import {Serialize, SerializeProperty, Serializable} from 'ts-serializer'; @Serialize({}) class User extends Serializable { @SerializeProperty({ map: 'first_name' }) firstName:string; @SerializeProperty({ map: 'last_name' }) lastName:string; } @Serialize({}) class Profile extends Serializable { @SerializeProperty({ type: User }) user: User; } let data = { user: { first_name: 'John', last_name: 'Doe' } }; let instance:Profile = new Profile(); instance.deserialize(data); console.log(instance.user.firstName); // Prints 'John' console.log(instance.user.lastName); // Prints 'Doe' console.log(instance.serialize()); // Prints {"user":{"first_name":"John", "last_name":"Doe"}} console.log(instance.user.serialize()); // Prints {"first_name":"John", "last_name":"Doe"}
For more information about the library check out serializer.dpopescu.me and ts-serializer Github page
Nice post! But I have a very awkward scenario, where my app should work offline. So what I am doing today is to send all the data as a json string to the client and sync it back when there is connection.
The problem I have is that data can become huge. When I JSON.parse a huge string on an old device, the browser crashes.
Is there any possible solution to this?
LikeLike
Personally, I would split my data in related chunks. You can still sync it with your server in just one call, but the JSON parsing can be done in chunks.
Let’s say you have this kind of data :
{user:{name:’a’, email:’a@b.com’}, books:[{name:’a’, type:’a’}, {name:’b’, type:’b’}], someOtherObject:{prop:value}}
Instead of parsing the whole thing you could just split it in small objects
var user = JSON.parse(data.user);
var books = JSON.parse(data.books);
var someObject = JSON.parse(data.someOtherObject);
When you want to send it back to the server just reverse the operation.
Hope this helps.
LikeLike
But you still need to do JSON.parse of that to have access to “user”, “books” and “someOtherObject” objects. So we’re back to the first problem 🙂
LikeLike
Save smaller json files locally. Instead of having one big json you will have smaller ones that can be easily parsed. User json, books json and someOtherObject json.
If you have some code publicly available maybe I can better understand what the code looks like.
LikeLike
You can still sync it with your server in just one call, but the JSON parsing can be done in chunks. User json, books json and someOtherObject json.
LikeLike
User json, books json and someOtherObject json. com’}, books:[{name:’a’, type:’a’}, {name:’b’, type:’b’}], someOtherObject:{prop:value}}
Instead of parsing the whole thing you could just split it in small objects
var user = JSON.
LikeLike
The problem I have is that data can become huge. User json, books json and someOtherObject json.
LikeLike
User json, books json and someOtherObject json. You can still sync it with your server in just one call, but the JSON parsing can be done in chunks.
LikeLike
User json, books json and someOtherObject json. com’}, books:[{name:’a’, type:’a’}, {name:’b’, type:’b’}], someOtherObject:{prop:value}}
Instead of parsing the whole thing you could just split it in small objects
var user = JSON.
LikeLike
getting error angular2-polyfills.js:138 Error: XHR error (404 Not Found) loading http://localhost:8000/ts-serializer
LikeLike
Hi Daniel, looks very interesting, especially it’s based on decorators. But I found no way to handle the deserializing with an angular 5 service. Would it possible to get a sample, too? greets, Michael
LikeLike