0.0.1-alpha.6 • Published 11 months ago

@arborjs/json v0.0.1-alpha.6

Weekly downloads
-
License
MIT
Repository
github
Last release
11 months ago

@arborjs/json

Provides a mechanism to easily serialize/deserialize custom user-defined types via an API similar to JSON.stringify and JSON.parse + some decorator "magic".

Installation

npm

npm install @arborjs/json

yarn

yarn add @arborjs/json

Usage

For most use-cases, the usage is extreamelly simple, with very little boilerplate:

  1. Decorate your class with the serializable decorator;
  2. Use the stringify function to serialize instances of the decorated class;
  3. Use the parse function to deserialize data strings representing instances of the decorated class.
import { serializable, stringify, parse } from "@arborjs/json"

@serializable
class Todo {
  constructor(readonly uuid: string, public text: string) {}
}

const todo = new Todo("my-uuid", "Clean the house")

const serialized = stringify(todo)
=> '{"$value":{"uuid":"my-uuid","text":"Clean the house"},"$type":"Todo"}'

const deserialized = parse(serialized)
expect(deserialized).toEqual(todo)
=> true

Managing multiple serializers

In case you need to manage multiple instances of the Json serializer in your app, you can:

  1. Create an instance of the Json class;
  2. Decorate your classes with the decorators provided by the Json instance;
  3. Leverage the Json#stringify to serialize your object;
  4. And Json#parse to deserialize the string data back into an instance of your serialized type.

Example:

  1. Instantiate the Json class:
import { Json } from `@arborjs/json`

// NOTE: we'll be referencing this variable in the following snippets
const json = new Json()
  1. Decorate your class so it can be known by the Json instance:
@json.serializable
class Todo {
  constructor(readonly uuid: string, public text: string) {}
}
  1. Serialize the object:
const todo = new Todo("my-uuid", "Clean the house")

const serialized = json.stringify(todo)
=> '{"$value":{"uuid":"my-uuid","text":"Clean the house"},"$type":"Todo"}'
  1. Deserialize the string data back into an instance of the Todo class:
const deserialized = json.parse(serialized)

expect(deserialized).toEqual(todo)
=> true

Custom serialization/deserialization logic

In certain situations, you will want to provide your own serialization/deserialization logic, here's how to achieve that:

@json.serializable
class TodoList extends Map<string, Todo> {
  constructor(...todos: Todo[]) {
    super(todos.map((todo) => [todo.uuid, todo]))
  }

  /*
   * Provide your own deserialization logic
   */
  static fromJSON(value: SerializedBy<TodoList>) {
    return new TodoList(...value)
  }

  /*
   * Provide your own serialization logic
   */
  toJSON() {
    return Array.from(this.values())
  }
}

Handling type name clashes

In case you end up with different types sharing the same name in your application, you can tell @arborjs/json which serialization keys to use to identify each type:

// models/users/Settings.ts
@json.serializableAs("UserSettings")
class Settings {
  constructor(
    readonly uuid: string,
    readonly userId: string,
    public active: boolean
  ) {}
}

// models/projects/Settings.ts
@json.serializableAs("ProjectSettings")
class Settings {
  constructor(
    readonly uuid: string,
    readonly projectId: string,
    public status: ProjectStatus
  ) {}
}

The serializableAs decorator instructs @arborjs/json to use the provided key to identify the decorated type so that it can differentiate different types with the same name in its registry, ultimatelly allowing proper serialization/deserialization of these types:

const userSettings = new UserSettings("user-settings-uuid", "user-id", true)
const projectSettings = new ProjectSettings("project-settings-uuid", "project-id", "in progress")

const serializedUserSettings = json.stringify(userSettings)
=> '{"$value":{"uuid":"user-settings-uuid","userId":"user-id","active":true},"$type":"UserSettings"}'

const serializedProjectSettings = json.stringify(projectSettings)
=> '{"$value":{"uuid":"project-settings-uuid","projectId":"project-id","status":"in progress"},"$type":"ProjectSettings"}'

Reducing boilerplate

You may choose to move the Json serializer setup into different modules in order to make its usage a little more friendly and with less boilerplate to the final user:

// src/json1.ts
import { Json } from "@arborjs/json"

const json = new Json()
export const parse = json.parse.bind(json)
export const stringify = json.stringify.bind(json)
export const serializable = json.serializable.bind(json)
export const serializableAs = json.serializableAs.bind(json)

// src/json2.ts
import { Json } from "@arborjs/json"

const json = new Json()
export const parse = json.parse.bind(json)
export const stringify = json.stringify.bind(json)
export const serializable = json.serializable.bind(json)
export const serializableAs = json.serializableAs.bind(json)

License

All packages in this monorepo ar MIT licensed.

0.0.1-alpha.6

11 months ago

0.0.1-alpha.5

11 months ago

0.0.1-alpha.4

11 months ago

0.0.1-alpha.3

11 months ago

0.0.1-alpha.2

11 months ago

0.0.1-alpha.1

11 months ago