Using ts-serializer to serialize and deserialize JSON Objects

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

Catching global errors in Angular 2

Angular 2 already has a very good error handler. When an error randomly occurs in your code, the Angular’s error handler will catch it and will print the error details in the console. The error details will also include the line number with a link to the source file. In most cases this should be more then enough to help you understand what is happening in your application.

Why would I need a custom error handler?

You can use a custom error handler to format the error messages that are logged into the browser’s console, to catch custom business errors in your application (Authentication/Authorization errors, HTTP 404, etc.) or maybe you want to also send the errors to you backend server for analytics or other reasons. All of the above are valid use cases of using a custom error handler.

Creating a custom error handler

Writing your own error handler is straightforward. You will need to extend Angular’s ErrorHandler class and override the handleError method. This method receives a wrapper of the original error as a parameter. You can find the original error in the error.originalError property. The default implementation uses console.error() to print the error details in the browser’s console.

CustomErrorHandler.ts

import {ErrorHandler} from '@angular/core';

export class CustomErrorHandler extends ErrorHandler {
    constructor(){
        super(false);
    }

    public handleError(error: any): void {
        // You can add your own logic here.
        // It is not required to delegate to the original implementation
        super.handleError(error);
    }
}

Extending Angular’s Error Handler

If you want to use your own custom handler you will need to somehow replace the one that Angular is using. Fortunately enough, Angular’s awesome DI engine will allows you to provide custom implementations for different classes.

AppModule.ts:

import {BrowserModule} from '@angular/platform-browser';
import {ErrorHandler} from '@angular/core';
import {AppComponent} from './app.component';

import {CustomErrorHandler} from './CustomErrorHandler';

@NgModule({
    declarations: [ AppComponent ],
    imports: [ BrowserModule ],
    bootstrap: [ AppComponent ],
    providers: [
        {provide: ErrorHandler, useClass: CustomErrorHandler}
    ]
})
export class AppModule {}

In the code above take a look at the providers field in the @NGModule decorator. With the above configuration in place, every time Angular’s DI engine will request an instance of ErrorHandler will receive an instance of our custom implementation CustomErrorHandler.

Creating custom error classes

Sometimes it’s useful to create custom error classes that you can throw in different parts of your application. For example, when the user is doesn’t have access to a specific part of the application you can throw a custom AuthorizationError.

Creating the custom error class

AuthorizationError.ts:

export class AuthorizationError {
    toString() {
        return 'You are not authorized to view this content!!!';
    }
}

Updating the CustomErrorHandler class

CustomErrorHandler.ts:

import {ErrorHandler} from '@angular/core';
import {AuthorizationError} from './AuthorizationError';

export class CustomErrorHandler extends ErrorHandler {
    constructor(){
        super(false);
    }

    public handleError(error: any): void {
        if(error.originalError instanceof AuthorizationError){
            console.info(`[CUSTOM ERROR]:::${error.originalError.toString()}`);
        } else {
            super.handleError(error);
        }
    }
}

Using the new error in your code

app.component.html:

<button (click)="throwCustomError()">Throw Authorization Error</button>

app.component.ts:

import {Component} from '@angular/core';
import {AuthorizationError} from './AuthorizationError';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  throwCustomError() {
    throw new AuthorizationError();
  }
}

Now when you press the Throw Authorization Error button you will see our custom error logging in the browser’s console:

[CUSTOM ERROR]:::You are not authorized to view this content!!!

If you look closely at the CustomErrorHandler class, everything that is not of type AuthorizationError will be delegated to the original Angular’s ErrorHandler.

Using HTTP Interceptors in Angular 2

What are HTTP Interceptors?

In Angular 1.x there was an option to register objects as HTTP Interceptors. You could then use those interceptors to perform different operations and transformations on all HTTP calls made by your application. This was a very powerful feature of the framework because not only allowed setting things like base path for the REST API endpoint, CSRF headers and many other things but also transforming or caching responses from the server.

So what’s different in Angular 2?

Well, after looking at the documentation you won’t be able to find any reference to interceptors in the new version of the framework. At least for now, interceptors are not currently supported out of the box. You could just use the classic way of doing things and just extend the HTTP class in Angular 2 but that’s not very pretty.

Introducing the XHRBackend

Angular 2 uses this class to create all HTTP connections. By taking advantage of the Angular’s great DI engine you could potentially extend the base XHRBackend class and provide our custom implementation to the application. By taking control of the creation of the HTTP connections you will be able to implement the classic Angular interceptors this way.

Let’s look at some code

Let’s start by creating our own XHRBackend.

MyXHRBackend.ts

import {XHRBackend, Request, XHRConnection, Response} from '@angular/http';
import {Observable} from 'rxjs';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';

export class MyXHRBackend extends XHRBackend {

  createConnection(request: Request): XHRConnection {
    let connection: XHRConnection = super.createConnection(request);
    // Before returning the connection we try to catch all possible errors(4XX,5XX and so on)
    connection.response = connection.response.catch(this.processResponse);
    return connection;
  }

  processResponse(response:Response){
    switch (response.status) {
      case 401:
        // You could redirect to login page here
        return Observable.throw('your custom error here');
      case 403:
        // You could redirect to forbidden page here
        return Observable.throw('your custom error here');
      case 404:
        // You could redirect to 404 page here
        return Observable.throw('your custom error here');
      default:
        return Observable.throw(response);
    }
  }

}

The above XHRBackend extension will catch all 401, 403 and 404 errors.
In order to tell Angular to use our implementation instead of the default class we will use Angular’s great DI features. We just need to add our custom class to the providers list in the application main module.

AppModule.ts

import {NgbModule} from '@ng-bootstrap/ng-bootstrap';
import {HttpModule, XHRBackend} from '@angular/http';
import {AppComponent} from './app.component';
import {MyXHRBackend} from './MyXHRBackend';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    HttpModule
  ],
  providers: [
    {provide: XHRBackend, useClass: MyXHRBackend}
  ],
  bootstrap: [AppComponent]
})
export class AppModule {
}

By providing our custom implementation of the XHRBackend, Angular’s HTTP class will use this implementation for all http calls. This means that if any http call encounters 401, 403 or 404 errors we can act accordingly in our application.