Skip to main content

🥣 ts_serialize tests release License: MIT

A zero dependency library for serializing data.

ts_serialize can help you with:

  • converting camelCase class members to snake_case JSON properties for use with a REST API
  • excluding internal fields from REST API payloads
  • converting data types to an internal format, for example: Date’s

Supported Serialize Types

Installing

Deno

import/export what you need from https://deno.land/x/ts_serialize@<version>/mod.ts in your deps.ts file. <version> will be a a tag found on the deno releases page. The version can be omitted to get the develop branch, however, for stability it is recommended to use a tagged version.

Example

Node

npm i @gamebridgeai/ts_serialize

Example

Usage

Basic

Import Serializable and SerializeProperty, extend Serializable with your class and use the SerializeProperty decorator on any properties you want serialized. Passing a string as an argument to SerializeProperty causes the property to use that name as the key when serialized.

SerializeProperty also excepts an options object with the properties:

  • serializedKey (Optional) {string} - Used as the key in the serialized object
  • toJsonStrategy (Optional) {ToJsonStrategy} - Function or ToJsonStrategy used when serializing
  • fromJsonStrategy (Optional) {FromJsonStrategy} - Function or FromJsonStrategy used when deserializing
class Test extends Serializable {
  @SerializeProperty()
  propertyOne = "Hello";
  @SerializeProperty("property_two")
  propertyTwo = "World!";
  notSerialized = "not-serialized";
}

assertEquals(new Test().toJson(), `{"propertyOne":"Hello","property_two":"World!"}`);
const testObj = new Test().fromJson(
  `{"propertyOne":"From","property_two":"Json!","notSerialized":"changed"}`
);
assertEquals(testObj.propertyOne, "From");
assertEquals(testObj.propertyTwo, "Json!");
assertEquals(testObj.notSerialized, "changed");
assertEquals(testObj.toJson(), `{"propertyOne":"From","property_two":"Json!"}`);

Transforming Keys

Serializable has a optional function tsTransformKey(key: string): string. This function can be provided to change all the keys without having to specify the change for each property.

class TestTransformKey extends Serializable implements TransformKey {
  public tsTransformKey(key: string): string {
    return `__${key}__`;
  }

  @SerializeProperty()
  public test = "test";
}

assertEquals(new TestTransformKey().toJson(), `{"__test__":"test"}`);
assertEquals(
  new TestTransformKey().fromJson({ __test__: "changed" }).test,
  `changed`,
);

tsTransformKey will be inherited by children

class TestTransformKey extends Serializable implements TransformKey {
  public tsTransformKey(key: string): string {
    return `__${key}__`;
  }
}

class TestTransformKey2 extends TestTransformKey {
  @SerializeProperty()
  public test2 = "test2";
}

class TestTransformKey3 extends TestTransformKey2 {
  @SerializeProperty()
  public test3 = "test3";
}

assertEquals(
  new TestTransformKey3().toJson(),
  `{"__test2__":"test2","__test3__":"test3"}`,
);
assertEquals(
  new TestTransformKey3().fromJson({ __test3__: "changed" }).test3,
  `changed`,
);

Children can also override their parent tsTransformKey function.

class TestTransformKey extends Serializable implements TransformKey {
  public tsTransformKey(key: string): string {
    return `__${key}__`;
  }
}

class TestTransformKey2 extends TestTransformKey {
  @SerializeProperty()
  public test2 = "test2";
}

class TestTransformKey3 extends TestTransformKey2 implements TransformKey {
  public tsTransformKey(key: string): string {
    return `--${key}--`;
  }
  @SerializeProperty()
  public test3 = "test3";
}

class TestTransformKey4 extends TestTransformKey3 {
  @SerializeProperty()
  public test4 = "test4";
}
assertEquals(
  new TestTransformKey4().toJson(),
  `{"__test2__":"test2","--test3--":"test3","--test4--":"test4"}`,
);
assertEquals(
  new TestTransformKey4().fromJson({ "--test4--": "changed" }).test4,
  `changed`,
);

If tsTransformKey is implemented and SerializeProperty is provided a serializedKey option, it will override the tsTransformKey function

class TestTransformKey extends Serializable implements TransformKey {
  public tsTransformKey(key: string): string {
    return `__${key}__`;
  }
}

class TestTransformKey2 extends TestTransformKey {
  @SerializeProperty()
  public test2 = "test2";

  @SerializeProperty("changed")
  public changeMe = "change me";

  @SerializeProperty({ serializedKey: "changed2" })
  public changeMe2 = "change me2";
}

assertEquals(
  new TestTransformKey2().toJson(),
  `{"__test2__":"test2","changed":"change me","changed2":"change me2"}`,
);
assertEquals(
  new TestTransformKey2().fromJson({ changed: "changed" }).changeMe,
  `changed`,
);
assertEquals(
  new TestTransformKey2().fromJson({ changed2: "changed" }).changeMe2,
  `changed`,
);

tsTransformKey is an efficient way to deal with camelCase to snake_case conversions.

Advanced

Strategies

Strategies are functions or a composed list of functions to execute on the values when serializing or deserializing. The functions take one argument which is the value to process.

const fromJsonStrategy = (v: string): BigInt => BigInt(v);
const toJsonStrategy = (v: BigInt): string => v.toString();

class Test extends Serializable {
  @SerializeProperty({
    serializedKey: "big_int",
    fromJsonStrategy,
    toJsonStrategy,
  })
  bigInt!: BigInt;
}

const testObj = new Test().fromJson(`{"big_int":"9007199254740991"}`);
assertEquals(testObj.bigInt.toString(), "9007199254740991");

Multiple strategy functions

toJsonStrategy and fromJsonStrategy can use composeStrategy to build out strategies with multiple functions.

const addWord = (word: string) => (v: string) => `${v} ${word}`;
const shout = (v: string) => `${v}!!!`;

class Test extends Serializable {
  @SerializeProperty({
    fromJsonStrategy: composeStrategy(addWord("World"), shout),
  })
  property!: string;
}

assertEquals(
  new Test().fromJson(`{"property":"Hello"}`).property,
  "Hello World!!!"
);

Dates

Dates can use the fromJsonStrategy to revive a serialized string into a Date object. ts_serialize provides a ISODateFromJson function to parse ISO Dates.

class Test extends Serializable {
  @SerializeProperty({
    fromJsonStrategy: ISODateFromJson,
  })
  date!: Date;
}

const testObj = new Test().fromJson(`{"date":"2020-06-04T19:01:47.831Z"}`);
assert(testObj.date instanceof Date);
assertEquals(testObj.date.getFullYear(), 2020);

createDateStrategy() can be use to make a reviving date strategy. Pass a regex to make your own.

class Test extends Serializable {
  @SerializeProperty({
    fromJsonStrategy: createDateStrategy(/^(\d{4})-(\d{2})-(\d{2})$/),
  })
  date!: Date;
}

const testObj = new Test().fromJson(`{"date":"2099-11-25"}`);
assert(testObj.date instanceof Date);
assertEquals(testObj.date.getFullYear(), 2099);

Inheritance

Inherited classes override the key when serializing. If you override a property any value used for that key will be overridden by the child value. With collisions the child always overrides the parent

class Test1 extends Serializable {
  @SerializeProperty("serialize_me")
  serializeMe = "nice1";
}

class Test2 extends Test1 {
  @SerializeProperty("serialize_me")
  serializeMeInstead = "nice2";
}

const testObj = new Test2();
assertEquals(testObj.serializeMe, "nice1");
assertEquals(testObj.serializeMeInstead, "nice2");
assertEquals(testObj.toJson(), `{"serialize_me":"nice2"}`);

Nested Class Serialization

ToJson:

class Test1 extends Serializable {
  @SerializeProperty("serialize_me_1")
  serializeMe = "nice1";
}

class Test2 extends Serializable {
  @SerializeProperty({
    serializedKey: "serialize_me_2",
  })
  nested: Test1 = new Test1();
}

const testObj = new Test2();
assertEquals(testObj.toJson(), `{"serialize_me_2":{"serialize_me_1":"nice1"}}`);

FromJson:

fromJsonAs is a provided function export that takes one parameter, the instance type the object will take when revived. fromJson is used to revive the object.

class Test1 extends Serializable {
  @SerializeProperty("serialize_me_1")
  serializeMe = "nice1";
}

class Test2 extends Serializable {
  @SerializeProperty({
    serializedKey: "serialize_me_2",
    fromJsonStrategy: fromJsonAs(Test1),
  })
  nested!: Test1;
}

const testObj = new Test2();
testObj.fromJson(`{"serialize_me_2":{"serialize_me_1":"custom value"}}`);
assertEquals(testObj.nested.serializeMe, "custom value");

Built With

Contributing

We have provided resources to help you request a new feature or report and fix a bug.

  • CONTRIBUTING.md - for guidelines when requesting a feature or reporting a bug or opening a pull request
  • DEVELOPMENT.md - for instructions on setting up the environment and running the test suite
  • CODE_OF_CONDUCT.md - for community guidelines

Versioning

We use SemVer for versioning.

Authors

See also the list of contributors who participated in this project.

License

This project is licensed under the MIT License - see the LICENSE file for details

Acknowledgments