A zero dependency library for serializing data.
ts_serialize can help you with:
- Converting
camelCaseclass members tosnake_caseJSON 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 Serialized Types:
ts_serialize supports both deno and node.
export what you need from https://deno.land/x/ts_serialize/mod.ts
The examples in this README pull from our
developbranch. You would want to "pin" to a particular version which is compatible with the version of Deno you are using and has a fixed set of APIs you would expect.https://deno.land/x/supports using git tags in the URL to direct you at a particular version. So to use version 2.0.0 ofts_serializeyou would want to importhttps://deno.land/x/ts_serialize@v2.0.0/mod.ts.
Install with npm i @gamebridgeai/ts_serialize or
yarn add @gamebridgeai/ts_serialize
To quickly get started extend Serializable with your class and add the
property decoratorSerializeProperty() to any class property you want in the serialization
process.
import{Serializable,SerializeProperty}from"./mod.ts";classMyClassextendsSerializable{
@SerializeProperty()publicmyProperty="Hello world!";}Serializable will add 5 methods. Each method has an implementable interface if
you wish to provide your own functionality.
fromJSONTakes one argument, the JSON
stringorJSONObjectto deserialize creating an object.fromJSONwill perform providedtsTransformKeyoperations and strategy value transformations.toJSONConverts the model to a JSON
stringand will perform providedtsTransformKeyoperations and strategy value transformations.cloneReturns a new reference of the object with all properties cloned, an optional parameter can be provided which is a
Partial<T>whereTis your class.tsSerializeConverts the model to "Plain old Javascript object" with any provided
tsTransformKeyor value transformationstsTransformKeyCalled against every key and has one parameter, the key to transform. The return value is a string. The default is to return the original parameter name. Key transformations will be inherited by children classes. Children classes can also
overridetheir parenttsTransformKeyfunction.
With this in mind we can write a more complex example. We'll make a base class
that provides a key transformation then add child classes.
import{Serializable,SerializeProperty,TransformKey}from"./mod.ts";abstractclassBaseextendsSerializableimplementsTransformKey{publicoverridetsTransformKey(key: string): string{return`__${key}__`;}}classParentextendsBase{
@SerializeProperty()publicparentProperty="Hello world!";}classChildOneextendsParent{
@SerializeProperty()publicchildOneProperty="Ahoy hoy world!";}classChildTwoextendsParentimplementsTransformKey{
@SerializeProperty()publicchildTwoProperty="Good Day world!";
@SerializeProperty("myCustomName")publicchildTwoPropertyTwo="Howdy world!";publicoverridetsTransformKey(key: string): string{return`--${key}--`;}}Passing a string or a function that returns a string as an argument to
SerializeProperty() causes the property to use that name as the key when
serialized. The function has one parameter, the key as string and should
return a string.
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
SerializeProperty() also excepts an optional options object with the
properties
serializedKey(Optional)
{string | ToSerializedKeyStrategy}A string value or function that has one parameter, the property key, and returns a string. The resulting value is used as the key in the serialized objecttoJSONStrategy(Optional)
{ToJSONStrategy}A function that has one parameter, the class property value and returns a value to be used when serialized asJSONfromJSONStrategy(Optional)
{FromJSONStrategy}A function that has one parameter, theJSONproperty value and returns a value to be used when serialized as a class
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.
import{Serializable,SerializeProperty}from"./mod.ts";constfromJSONStrategy=(v: string): BigInt=>BigInt(v);consttoJSONStrategy=(v: BigInt): string=>v.toString();classTestextendsSerializable{
@SerializeProperty({serializedKey: "big_int",
fromJSONStrategy,
toJSONStrategy,})publicbigInt!: BigInt;}toJSONStrategy and fromJSONStrategy can use composeStrategy to build out
strategies with multiple functions.
import{composeStrategy,Serializable,SerializeProperty}from"./mod.ts";constaddWord=(word: string)=>(v: string)=>`${v}${word}`;constshout=(v: string)=>`${v}!!!`;classTestextendsSerializable{
@SerializeProperty({fromJSONStrategy: composeStrategy(addWord("World"),shout),})publicproperty!: string;}Dates can use the fromJSONStrategy to revive a serialized string into a Date
object. ts_serialize provides a iso8601Date function to parse ISO Dates.
import{iso8601Date,Serializable,SerializeProperty}from"./mod.ts";classTestextendsSerializable{
@SerializeProperty({fromJSONStrategy: iso8601Date(),})publicdate!: Date;}createDateStrategy() can be used to make a reviving date strategy. Pass a
regex to make your own. The example below uses a yyyy-mm-dd format to
construct a Date
import{createDateStrategy,Serializable,SerializeProperty}from"./mod.ts";classTestextendsSerializable{
@SerializeProperty({fromJSONStrategy: createDateStrategy(/^(\d{4})-(\d{2})-(\d{2})$/),})publicdate!: Date;}While @SerializeProperty is handy with to and from JSON strategies, it can
still be verbose to declare the strategies for each property. You can define
your own decorator functions to wrap @SerializeProperty and provide the
toJSONStrategy and fromJSONStrategy. An example short cut is providing a
type to use with toSerializable. getNewSerializable is provided to allow a
raw serializable type or a function that returns a constructed serializable type
enabling constructor arguments:
import{getNewSerializable,Serializable,SerializeProperty,toSerializable,}from"./mod.ts";functionDeserializeAs(type: unknown,): PropertyDecorator{returnSerializeProperty({fromJSONStrategy: toSerializable(()=>getNewSerializable(type)),});}classAextendsSerializable{
@SerializeProperty("property_a")publicproperty="";}classBextendsSerializable{
@DeserializeAs(A)publicproperty=newA();publicotherProperty="";constructor({ otherProperty =""}: Partial<B>={}){super();this.otherProperty=otherProperty;}}classCextendsSerializable{
@DeserializeAs(()=>newB({otherProperty: "From Class C"}))publicproperty=newB();}The @PolymorphicResolver and @PolymorphicSwitch decorators can be used to
cleanly handle deserializing abstract types into their constituent
implementations.
The @PolymorphicSwitch() decorator is a quick way to serialize simple
polymorphic types based on the properties of a child class.
Note that @PolymorphicSwitch() can only be applied to child classes
deserializing from their direct parent class.
Properties decorated with @PolymorphicSwitch() must also be serializable
properties. The from JSON strategy and associated serialized key of that
property will be used when comparing the value.
import{polymorphicClassFromJSON,PolymorphicSwitch,Serializable,SerializeProperty,}from"./mod.ts";enumColour{RED="RED",BLUE="BLUE",}abstractclassMyColourClassextendsSerializable{}classMyRedClassextendsMyColourClass{
@SerializeProperty()
@PolymorphicSwitch(()=>newMyRedClass(),Colour.RED)publiccolour=Colour.RED;
@SerializeProperty()publiccrimson=false;}constredClass=polymorphicClassFromJSON(MyColourClass,`{"colour":"RED","crimson":true}`,);You can also provide a test function instead of a value to check if the value for the annotated property satisfies a more complex condition:
import{polymorphicClassFromJSON,PolymorphicSwitch,Serializable,SerializeProperty,}from"./mod.ts";abstractclassCurrencyextendsSerializable{}classDollarCurrencyextendsCurrency{
@SerializeProperty()
@PolymorphicSwitch(()=>newDollarCurrency(),"$")publiccurrencySymbol="$";
@SerializeProperty()publicamount=0;}classOtherCurrencyextendsCurrency{
@SerializeProperty()
@PolymorphicSwitch(()=>newOtherCurrency(),(value)=>value!=="$",)publiccurrencySymbol="";
@SerializeProperty()publicamount=0;}constcurrencyClass=polymorphicClassFromJSON(Currency,`{"currencySymbol":"£","amount":300}`,);Multiple @PolymorphicSwitch annotations can be applied to a single class, if
necessary
import{polymorphicClassFromJSON,PolymorphicSwitch,Serializable,SerializeProperty,}from"./mod.ts";abstractclassMyAbstractClassextendsSerializable{}classMyClassextendsMyAbstractClass{
@SerializeProperty()
@PolymorphicSwitch(()=>newMyClass(),"$")
@PolymorphicSwitch(()=>newMyClass(),"dollar")
@PolymorphicSwitch(()=>newMyClass(),(value)=>typeofvalue==="string"&&value.includes("dollars"),)publicmyProperty="$";
@SerializeProperty()publicamount=0;}constmyClass1=polymorphicClassFromJSON(MyAbstractClass,`{"myProperty":"300 dollars"}`,);constmyClass2=polymorphicClassFromJSON(MyAbstractClass,`{"myProperty":"dollar"}`,);The following example shows how the @PolymorphicResolver decorator can be used
to directly determine the type of an abstract class implementor, which will then
be used when deserializing JSON input.
import{polymorphicClassFromJSON,PolymorphicResolver,Serializable,SerializeProperty,}from"./mod.ts";enumColour{RED="RED",BLUE="BLUE",}abstractclassMyColourClassextendsSerializable{
@SerializeProperty()publiccolour?: Colour;
@PolymorphicResolver()publicstaticresolvePolymorphic(input: string): MyColourClass{constcolourClass=newPolymorphicColourClass().fromJSON(input);switch(colourClass.colour){caseColour.RED:
returnnewMyRedClass();caseColour.BLUE:
returnnewMyBlueClass();default:
thrownewError(`Unknown Colour ${colourClass.colour}`);}}}classPolymorphicColourClassextendsMyColourClass{}classMyRedClassextendsMyColourClass{
@SerializeProperty()privatecrimson=false;publicisCrimson(): boolean{returnthis.crimson;}}classMyBlueClassextendsMyColourClass{
@SerializeProperty()privateaqua=false;publicisAqua(): boolean{returnthis.aqua;}}constredClass=polymorphicClassFromJSON(MyColourClass,`{"colour":"RED","crimson":true}`,);- Deno 🦕
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
We use SemVer for versioning.
- Scott Hardy - Initial work - @hardy925 🐸
- Chris Dufour - Initial work - @ChrisDufourMB 🍕 🐱 👑
See also the list of contributors who participated in this project.
This project is licensed under the MIT License - see the LICENSE file for details
- Our colleagues at MindBridge for discussion and project planning
- Parsing Dates with JSON for knowledge
- OAK Server as a project structure example