# serde-ts a library for serializing and deserializing javascript objects # Usage this library makes no assumptions about formats and loosely follows the architecture of Rust's [serde](https://serde.rs/) crate. the major difference are the particular data types. serde-ts's internal data model contains the following types ## `serde-ts` data types * null/undefined * boolean * number * bigint * string * symbol * function * object * iterable * class with the exception of iterables and classes, these types were chosen as they are the ones provided by Javascript's native `typeof` operator which is used internally for type checking. there are four interfaces which are relevant to `serde-ts` users. ## `Serializer` ```ts interface ISerializer { serializeAny?(value: any): T serializeBoolean(value: boolean): T serializeNumber(value: number): T serializeBigInt(value: bigint): T serializeString(value: string): T serializeSymbol(value: symbol): T serializeNull(): T serializeObject(): ISerializeObject serializeClass(name: string): ISerializeObject } ``` a `Serializer` is responsible for transforming the internal `serde-ts` data model into the target serialization format. this means that it will have a `serialize` function for each of `serde-ts`'s data types. ## `Deserializer` ```ts interface IDeserializer { deserializeAny(visitor: Partial>): T deserializeBoolean(visitor: Partial>): T deserializeNumber(visitor: Partial>): T deserializeBigInt(visitor: Partial>): T deserializeString(visitor: Partial>): T deserializeSymbol(visitor: Partial>): T deserializeNull(visitor: Partial>): T deserializeObject(visitor: Partial>): T deserializeIterable(visitor: Partial>): T deserializeFunction(visitor: Partial>): T } ``` a `Deserializer` is responsible for transforming the target serialization format into the internal `serde-ts` data model. it will have a `deserialize` for each of the `serde-ts`'s data types. ## `Serialize` ```ts interface Serialize { (serializer: ISerializer, value: U): T } ``` `Serialize` is responsible for transforming a value *to* the internal data model. for example, a `Vector2` may be internally serialized as an iterable of numbers. ## `Deserialize` ```ts interface Deserialize { (deserializer: IDeserializer): T } ``` `Deserialize` is responsible for transforming a value *from* the internal data model. it is the inverse of `Serialize`. depending on the `Deserialize` target, it may accept an iterable of numbers and produce a `Vector2`. # Example in simple cases, a formatter for `serde-ts` will expose a pair of functions like `toString` and `fromString` which will handle serializing and deserializing for you. in more complex cases, you still won't be implementing `Serializer`/`Deserializer`, only consuming them with `Serialize`/`Deserialize`. assuming we're using a format which reads/writes JSON ```ts import { registerSerialize, registerDeserialize } from 'serde' import { ISerializer } from 'serde/ser' import { IDeserializer, IIterableAccess } from 'serde/de' import { fromString, toString } from 'serde-json-ts' class Vector2 { x: number y: number constructor(x: number, y: number) { this.x = x this.y = y } } // we're registering to the global serde registry registerSerialize(Vector2, (serializer: ISerializer, value: Vector2) => { const iter = serializer.serializeIterable() // returns an ISerializeIterable iter.serializeElement(value.x) iter.serializeElement(value.y) return iter.end() }) registerDeserialize(Vector2, (deserializer: IDeserializer) => deserializer.deserializeIterable({ // we could implement visitNumber here, but we'll let the default // deserializer handle it visitIterable(access: IIterableAccess) { const elements = [] for (const item of access) { elements.push(item as number) } return new Vector2(elements[0], elements[1]) } }) ) const one = new Vector2(1, 1) const serialized = toString(one) console.log(serialized) // "[1, 1]" const deserializedOne = fromString(serialized, Vector2) console.log(deserializedOne) // Vector2 { x: 1, y: 1 } ```