loading...
Cover image for Introducing 🥣 ts_serialize

Introducing 🥣 ts_serialize

hardy613 profile image Scott Hardy Updated on ・3 min read

Hello Dev.to!

ts_serialize is a new module for serialization that helps with:

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

It is a zero dependency module for Deno and Node.

Installing with Deno

export what you need from https://deno.land/x/ts_serialize@<version>/mod.ts in your deps.ts file. <version> will be a tag found on the deno releases page.

// deps.ts
export {
  Serializable,
  SerializeProperty,
  composeStrategy,
  fromJsonAs,
  FromJsonStrategy,
  ToJsonStrategy,
  ISODateFromJson,
  createDateStrategy,
} from "https://deno.land/x/ts_serialize@0.2.4/mod.ts";

Example

Installing with Node

npm i @gamebridgeai/ts_serialize

Example

Usage

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.

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!"}`);

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

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");

The module is FOSS. Issues can be reported and we are accepting RFC's and PR's for new features.

Thanks for reading and let me know what you think in the comments.

Posted on by:

Discussion

markdown guide