Serde usage
Initial configuration
import { Serde } from "@daiso-tech/core/serde";
import { SuperJsonSerdeAdapter } from "@daiso-tech/core/serde/adapters";
const serde = new Serde(new SuperJsonSerdeAdapter());
Serde basics
Serializing and deserializing values
Here is an example of serializing and deserializing a value.
const serializedValue = serde.serialize({
name: "abra",
age: 20,
});
const deserializedValue = serde.deserialize(serializedValue);
Custom serialization and deserialization logic of classes
Serializing and deserializing a class:
import type { ISerializable } from "@daiso-tech/core/serde/contracts";
type ISerializedUser = {
name: string;
age: number;
};
class User implements ISerializable<ISerializedUser> {
static deserialize(serializedUser: ISerializedUser): User {
return new User(serializedUser.name, serializedUser.age);
}
constructor(
public readonly name: string,
public readonly age: number,
) {}
serialize(): ISerializedUser {
return {
name: this.name,
age: this.age,
};
}
logInfo(): void {
console.log("Name:", this.name, "Age:", this.age);
}
}
serde.registerClass(User);
const user = new User("Carl", 50);
const serializedUser = serde.serialize(user);
const deserializedUser = serde.deserialize(serializedUser);
// The instances will not be the same because deserializedUser is recreated.
console.log(user === deserializedUser);
// But the content will be the same
deserializedUser.logInfo();
user.logInfo();
Note you need to register the class before serialzing or deserialzing any class instances.
To ensure correct serialization and deserialization, class names must be unique. If multiple classes share the same name, conflicts may occur when serialzing and deserialzing the objects. To resolve this, you can assign a unique prefix to differentiate between them during the process.
serde.registerClass(User, "my-library");
Custom serialization and deserialization logic
The registerClass
method provides a simplified abstraction over registerCustom
method, which offers finer-grained control over serialization and deserialization behavior.
import type { ISerdeTransformer } from "@daiso-tech/core/serde/contracts";
type ISerializedUser = {
name: string;
age: number;
};
const userSerdeTransformer: ISerdeTransformer<User, ISerializedUser> = {
name: User.name,
isApplicable(value: unknown): value is User {
return value instanceof User;
}
deserialize(serializedValue: TSerializedValue): User {
return new User(serializedValue.name, serializedValue.age);
}
serialize(deserializedValue: User): TSerializedValue {
return {
name: user.name,
age: user.age,
};
}
}
serde.registerCustom(userSerdeTransformer);
Note the ISerdeTranformer
object can be dynamically created.
Patterns
Usage with other components
When using Serde
class instance there is no need to call serialize
and deserialize
manually. Because components like Cache
handle it automatically through their adapter.
import { Serde } from "@daiso-tech/core/serde";
import { Namespace } from "@daiso-tech/core/utilities";
import { SuperJsonSerdeAdapter } from "@daiso-tech/core/serde/adapters";
import { RedisCacheAdapter } from "@daiso-tech/core/cache/adapters";
import { Cache } from "@daiso-tech/core/cache";
import { ListCollection } from "@daiso-tech/core/collection";
import Redis from "ioredis";
const serde = new Serde(new SuperJsonSerdeAdapter());
serde.registerClass(ListCollection);
const cache = new Cache({
namespace: new Namespace("cache"),
adapter: new RedisCacheAdapter({
database: new Redis("YOUR_REDIS_CONNECTION_STRING"),
serde,
}),
});
const listCollection = new ListCollection(["a", "b", "c", "d", "e"]);
await cache.add("list", listCollection);
const deserializedListCollection = await cache.get("list");
if (deserializedListCollection) {
// Logs "c"
console.log(deserializedListCollection.getOrFail(2));
}
Note you should use one Serde
class instance accross all components and register all serializable objects before component usage.
Seperating serialization, deserialization and registering custom serialization/deserialization logic
The library includes 4 additional contracts:
-
ISerializer
- Allows only for serialization. -
IDeserializer
- Allows only for deserialization. -
ISerde
- Allows for both serialization and deserialization. -
IFlexibleSerde
– Allows for both serialization and deserialization. Allows also for customizable serialization/deserialization logic. -
ISerderRegister
- Allows only for regestering custom serialization and deserialization logic.
This seperation makes it easy to visually distinguish the 4 contracts, making it immediately obvious that they serve different purposes.