Skip to content

A typescript library to serialize/deserialize classes to/from string in a flat format. Supports inheritance, circular reference and more

License

Notifications You must be signed in to change notification settings

ynixt/typescript-flat-serializer

Repository files navigation

typescript-flat-serializer

Build status Coverage Status Known Vulnerabilities npm bundle size GitHub npm

A typescript library to serialize/deserialize classes to/from string in a flat format. Supports inheritance, circular reference and more

Summary

  1. Why
  2. Installation
  3. Usage
  4. API
  5. Important notes
  6. Inspired by

Why

When I was developing a electron app I needed a serializer that fill some requirements to use with IPC:

  • After serialize I wanted that the object keep the methods (right prototype)
  • I needed inheritance
  • Supports circular reference is always good

Installation

npm install typescript-flat-serializer --save

Usage

With decorators

Preparation

If you want to use our decorators you also need to set experimentalDecorators and emitDecoratorMetadata to true into the tsconfig.json file.

For example:

{
  "compilerOptions": {
    ...
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    ...
  }
}

Usage

First let's create some models

import { TSFlatCollection, TSFlatObject } from "typescript-flat-serializer";

@TSFlatObject()
export abstract class Animal {
  protected constructor(public name: string) {
  }
}

// This decorator NEEDS to be placed on every class that you want to serialize. 
// Without this decorator the behavior will be like stringify/parse from JSON.
@TSFlatObject()
export class Dog extends Animal {
  // This decorator will take care of serialize/deserialize our collection
  @TSFlatCollection({collectionType: "set"})
  favoriteFoods: Set<Food>;

  constructor(name: string, public beautiful: boolean, favoriteFoods: Set<Food>) {
    super(name);
    this.favoriteFoods = favoriteFoods;
  }
}

@TSFlatObject()
export class Food extends Animal {
  constructor(name: string) {
    super(name);
  }
}

Now we only need to serialize/deserialize the animal.

import { parse, stringify } from "typescript-flat-serializer";


const foods = new Set([new Food('all')])
const animal: Animal = new Dog('Luffy', true, foods);

// Let's go serialize our animal
const str = stringify(animal)
// value of str: [{"name":"Luffy","beautiful":true,"favoriteFoods":"#_1_#","__class__":"Dog"},["#_2_#"],{"name":"all","__class__":"Food"}]

// And now we can deserialize the animal
const parsedAnimal = parse<Animal>(str);

You can find another examples of utilisation on tests.

Without decorators

First let's create some models

import { registerTSFlat } from "typescript-flat-serializer";

export abstract class Animal {
  protected constructor(public name: string) {
    registerTSFlat(
      {
        target: Animal,
      }
    );
  }
}

export class Dog extends Animal {
  favoriteFoods: Set<Food>;

  constructor(name: string, public beautiful: boolean, favoriteFoods: Set<Food>) {
    super(name);
    registerTSFlat(
      {
        target: Dog,
      },
      { collectionName: 'favoriteFoods', options: {collectionType: 'set'} }
    )
    this.favoriteFoods = favoriteFoods;
  }
}

export class Food extends Animal {
  constructor(name: string) {
    super(name);
    registerTSFlat(
      {
        target: Food,
      },
    )
  }
}

Now we only need to serialize/deserialize the animal.

import {parse, stringify} from "typescript-flat-serializer";


const foods = new Set([new Food('all')])
const animal: Animal = new Dog('Luffy', true, foods);

// Let's go serialize our animal
const str = stringify(animal)
// value of str: [{"name":"Luffy","beautiful":true,"favoriteFoods":"#_1_#","__class__":"Dog"},["#_2_#"],{"name":"all","__class__":"Food"}]

// And now we can deserialize the animal
const parsedAnimal = parse<Animal>(str);

API

Decorators

@TSFlatObject

Used to make a class serializable.

Example
@TSFlatObject()
export class Dog {
}

@TSFlatCollection

Used do make a Array|Set|Map|Dictionary

Example
@TSFlatObject()
export class Dog {
  @TSFlatCollection({collectionType: "map"})
  placesVisited: Map<string, boolean>
}
Parameters

collectionType Type: CollectionTypeString
Optional: false
Description: The way to specify the type of collection

@TSFlatProperty

Used modify the serialization/deserialization of a property.

Example
@TSFlatObject()
export class Dog {
  @TSFlatCollection({collectionType: "map"})
  @TSFlatProperty({
    beforeStringify: (m) => {
      m.set('Brazil', true);
    }
  })
  placesVisited: Map<string, boolean>
}
Parameters

options Type: TSFlatPropertyOptions
Optional: true
Description: The option to customize the serialization/deserialization of the target property.

Methods

registerTSFlat

Used do register an object and its items.

registerTSFlat<T>(objectRegistration: FlatObjectRegistration<T>, ...items: FlatItem<T>[])
Parameters

objectRegistration

Type: FlatObjectRegistration<T>
Optional: false
Description: Information about the class that we want to make serializable.

Type: FlatItem<T>[]
Optional: true
Description: Items that this class have that we want to make serializable.


stringify

Used to serialize a object.

stringify(obj: any, options?: StringifyOptions): string
Parameters

obj

Type: any
Optional: false
Description: The object that will be serialized

options

Type: StringifyOptions
Optional: true
Description: Custom options to the serialization

Return

string


parse

Used to deserialize a string into a object.

parse<T>(str: string): T
Return

T

Definitions

Types

FlatObjectRegistration
export type FlatObjectRegistration<T> = {
  target: Type<T>,
  options?: TSFlatObjectProperties
}
FlatItem<T>
export type FlatItem<T> = FlatPropertyRegistration<T> | FlatCollectionRegistration<T>;
FlatPropertyRegistration<T>
export type FlatPropertyRegistration<T> = {
  propertyName: (keyof T), options?: TSFlatPropertyOptions,
}
FlatCollectionRegistration<T>
export type FlatCollectionRegistration<T> = {
  collectionName: (keyof T), options: TSFlatCollectionOptions
}
CollectionTypeString
export type CollectionTypeString = 'array' | 'dictionary' | 'map' | 'set';
TSFlatPropertyOptions
export type PropertyTransformer = (property: any) => any;

export interface TSFlatPropertyMetadata {
    beforeStringify?: PropertyTransformer;
    afterParse?: PropertyTransformer;
}

export interface TSFlatPropertyOptions extends TSFlatPropertyMetadata {
}

stringifyOptions

export type StringifyOptions = {
  rFDCOptions?: RFDCOptions
}

export type CustomWayOfCloningObjectMap = Map<Type<any>, (obj: any) => any>;

export type RFDCOptions = {
  customWayOfCloningObject?: CustomWayOfCloningObjectMap
}

Important notes

Cloning

This library before the serialization makes a clone of the object. By default the cloning supports the types:

  • Object
  • Array
  • Number
  • String
  • null
  • Date
  • undefined
  • Buffer
  • TypedArray
  • Map
  • Set
  • Function
  • AsyncFunction
  • GeneratorFunction
  • arguments

To support other type, like DateTime of Luxon, you should do something like that:

const customWayOfCloningObject: CustomWayOfCloningObjectMap = new Map();
const rFDCOptions: RFDCOptions = {
  customWayOfCloningObject
}

customWayOfCloningObject.set(DateTime, (obj) => DateTime.fromMillis(obj.toMillis()));

const str = stringify(obj, {rFDCOptions});

Inspired by

About

A typescript library to serialize/deserialize classes to/from string in a flat format. Supports inheritance, circular reference and more

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published