diff --git a/README.md b/README.md new file mode 100644 index 0000000..6f2c708 --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +# 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` + +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` + +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` + +`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` + +`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`. + + + + diff --git a/src/ser/impl.ts b/src/ser/impl.ts index 400e5db..13fe7cb 100644 --- a/src/ser/impl.ts +++ b/src/ser/impl.ts @@ -1,5 +1,6 @@ -import { ISerializeObject, ISerializer } from './interface' -import { isPlainObject } from '../utils' +import { ISerializeObject, ISerializer, Serialize } from './interface' +import { isObject, isPlainObject, PrimitivePrototype } from '../utils' +import { GlobalRegistry, Registry } from '../registry' class UnhandledTypeError extends TypeError { constructor(serializer: ISerializer, value: any) { @@ -7,7 +8,7 @@ class UnhandledTypeError extends TypeError { } } -function serializeObject>(serializer: S, obj: V): T { +function serializeObject>(serializer: S, obj: U): T { for (const key in obj) { const value = obj[key] serializer.serializeEntry(key, value) @@ -16,13 +17,21 @@ function serializeObject>(ser return serializer.end() } -function serializeClass>(serializer: S, value: V): T { +function serializeClass>(serializer: S, value: U): T { const name = value.constructor.name const ser = serializer.serializeClass(name) return serializeObject(ser, value) } -export function serialize>(serializer: S, value: V): T { +function getSerialize(value: U, registry: Registry): Serialize { + if (isObject(value)) { + return registry.serializers.get(value.constructor) as Serialize + } + + return registry.serializers.get(PrimitivePrototype[typeof value]) || defaultSerialize as Serialize +} + +function defaultSerialize>(serializer: S, value: U): T { switch (typeof value) { case 'string': return serializer.serializeString(value) case 'number': return serializer.serializeNumber(value) @@ -40,3 +49,8 @@ export function serialize>(serializer: S, value: } } +export function serialize>(serializer: S, value: U, registry: Registry = GlobalRegistry): T { + const ser = getSerialize(value, registry) + return ser(serializer, value) +} + diff --git a/src/ser/interface.ts b/src/ser/interface.ts index a7b37b9..9fd5462 100644 --- a/src/ser/interface.ts +++ b/src/ser/interface.ts @@ -77,6 +77,6 @@ export class Serializer implements ISerializer { } export interface Serialize { - >(serializer: S, value: T): U + >(serializer: S, value: T): U } diff --git a/src/utils.ts b/src/utils.ts index 23e8b06..877fd86 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -10,7 +10,11 @@ export interface ToString { toString(): string } -export function isPlainObject(value: any): boolean { +export function isObject(value: any): value is object { + return typeof value === 'object' +} + +export function isPlainObject(value: any): value is object { return Object.getPrototypeOf(value) === Object.prototype } @@ -42,3 +46,18 @@ export class IterResult { } } +export function Null(...args: any) { + return null +} + +export const PrimitivePrototype = Object.freeze({ + undefined: Null, + boolean: Boolean, + number: Number, + bigint: BigInt, + string: String, + symbol: Symbol, + object: Object, + function: Function +} as const) +