loading...

Enhance serialization with @kaiu/serializer

supamiu profile image Flavien Normand ・3 min read

In today's frontend applications, there's one thing we almost all have in common: we want to serialize JSON strings into objects.

Thanks to the amazing tool that JSON is in Javascript, this became an easy and safe way to handle serialization inside our applications.

But what if your objects need to be an instance of a class?

What?

In a project, we had an issue that I think many developpers are facing: having methods inside objects that you're getting from an API.

Let's say you have a Foo class inside your typescript application:

export class Foo {
    bar: string;
    baz: number;

    getBarBaz():string {
        return `${this.bar}${this.baz}`;
    }
}

If you get an object that comes from an endpoint in which you're storing foos, you want the models to have the barBarBaz() method.

Let's say you have this JSON coming from an API:

{
    "bar": "example",
    "baz": 0
}

You will want it to end up as an instance of Foo, so you'll end up having to create manual mapping for each class you want to persist inside the API, something like this:

export class Foo implements APIModel {
    bar: string;
    baz: number;

    getBarBaz():string {
        return `${this.bar}${this.baz}`;
    }

    hydrateFromJSONObject(obj: Partial<Foo>):Foo {
        this.bar = obj.bar;
        this.baz = obj.baz;
    }
}

But then you start to work with more complex structures, with complex field that are class instances themselves, or even arrays of class instances. And things start to become quite difficult to handle without having to write a lot of repetitive code.

But there's a solution !

Because we found this hard to maintain, we started thinking about a solution that uses the power of decorators to make all this easier for everyone.

That's why we created @kaiu/serializer, a simple library to deal with this easily, using simple decorators.

Example

import { Serializer } from '@kaiu/serializer';

const serializer = new Serializer();
const foo: Foo = serializer.deserialize<Foo>(JSON.parse(jsonString), Foo);

console.log(foo.getBarBaz()); // Will properly print "example0" if given the example json.

There is some super cool decorators for specific use cases, like @FieldName (docs) which allows you to map a field to a different name in your application, or @DeserializeAs (docs) which allows you to map a field to a class instance.

Some advanced use cases are also implemented, like inheritance handling, so you can have a parent class with multiple children classes, the serializer will be able to use the matching child class based on a given field value (docs)

For the angular devs

As our initial use case was for an Angular application, we created a simple wrapper that provides the serializer as a service in a module, with easy configuration injection. You can find it on npmjs: @kaiu/ng-serializer

Feel free to star the project on github or create issues and PRs, they are always welcome 😄

If you have any question, please don't hesitate to ask them in the comments or ping me on twitter: @Supamiu_.

Posted on by:

supamiu profile

Flavien Normand

@supamiu

Frontend dev @ Zenika, Angular Lover, Hardcore raider and Open Source gaming tools creator !

Discussion

markdown guide