A comprehensive Serializer/Deserializer that can handle any data type.
deno add jsr:@denostack/superserialimport { Superserial } from "@denostack/superserial";# npm
npm install superserial
# bun
bun add superserial
# deno
deno add npm:superserialimport { Superserial } from "superserial";The serialized output format has been updated for performance optimization and introduces a breaking change when upgrading from
0.3.xto0.4.x. Data serialized withsuperserialversions0.3.x(or earlier) cannot be deserialized with version0.4.x(or later).
Value Properties
NaN-0,+0Infinity,-Infinityundefined
Fundamental Objects
Symbol
ETC
BigIntDateRegExpMapSet
const superserial = new Superserial();
const symbol = Symbol();
const serialized = superserial.serialize({
nzero: -0,
und: undefined,
nan: NaN,
inf: Infinity,
ninf: -Infinity,
sym: symbol,
bigint: 100n,
date: new Date(),
regex: /abc/gmi,
map: new Map([["key1", "value1"], ["key2", "value2"]]),
set: new Set([1, 2, 3, 4]),
});
const deserialized = superserial.deserialize(serialized);
/*
{
nzero: -0,
und: undefined,
nan: NaN,
inf: Infinity,
ninf: -Infinity,
sym: Symbol(), // Symbol but not exact same with original
bigint: 100n,
date: 2025-12-15T09:05:09.108Z, // ... Date!
regex: /abc/gim,
map: Map(2) { "key1" => "value1", "key2" => "value2" },
set: Set(4) { 1, 2, 3, 4 }
}
*/Existing JSON functions do not support circular references, but superserial has solved this problem.
const nodes = [{ self: null as any, siblings: [] as any[] }, {
self: null as any,
siblings: [] as any[],
}];
nodes[0].self = nodes[0];
nodes[0].siblings = nodes;
nodes[1].self = nodes[1];
nodes[1].siblings = nodes;
const serialized = superserial.serialize(nodes);
console.log(serialized);
// [[[[0,1],[0,2]]],{"self":[0,1],"siblings":[0,0]},{"self":[0,2],"siblings":[0,0]}]
const deserialized = superserial.deserialize(serialized) as typeof nodes;
console.log(deserialized === deserialized[0].siblings); // true
console.log(deserialized[0] === deserialized[0].self); // true
console.log(deserialized === deserialized[1].siblings); // true
console.log(deserialized[1] === deserialized[1].self); // trueCircular Set & Map
const set = new Set();
set.add(set);
superserial.serialize(set); // [["Set",[0,0]]]
const map = new Map();
map.set(map, map);
superserial.serialize(map); // [["Map",[[[0,0],[0,0]]]]]Deserialization also works perfectly!
const set = superserial.deserialize('[["Set",[0,0]]]') as Set<unknown>;
console.log(set === [...set][0]); // true
const map = superserial.deserialize('[["Map",[[[0,0],[0,0]]]]]') as Map<unknown, unknown>;
console.log(map === [...map.keys()][0]); // true
console.log(map === map.get([...map.keys()][0])); // trueClasses contain methods, getters, etc., but JSON doesn't fully support them. superserial allows you to preserve the class instance, including all its methods and internal state.
The easiest way to register a class is using the @serializable decorator. You need to enable the decorator option
when creating the Superserial instance.
import { serializable, Superserial } from "superserial";
@serializable()
class TestUser {
constructor(public name: string, public age: number) {}
greet() {
return `Hello, ${this.name}`;
}
}
// Enable decorator support
const superserial = new Superserial({ decorator: true });
const serialized = superserial.serialize(new TestUser("wan2land", 20));
console.log(serialized);
// [["TestUser",{"name":"wan2land","age":20}]]
const user = superserial.deserialize<TestUser>(serialized);
console.log(user instanceof TestUser); // true
console.log(user.greet()); // "Hello, wan2land"You can also specify a custom name (alias) or custom serialization logic directly in the decorator.
@serializable("MyUser") // Alias
class TestUser {/* ... */}
@serializable({
toSerialize: (user) => ({ n: user.name }), // Custom logic
toDeserialize: (value) => new TestUser(value.n, 0),
})
class CompactUser {/* ... */}If you need fine-grained control, such as handling private fields (#private) or transforming data, you can implement
the toSerialize and toDeserialize symbols within your class. This keeps the serialization logic encapsulated within
the class.
import { Superserial, toDeserialize, toSerialize } from "superserial";
class SecureUser {
#age = 0;
constructor(public name: string) {
this.#age = 0;
}
setAge(age: number) {
this.#age = age;
}
getAge() {
return this.#age;
}
// Define what to save
[toSerialize]() {
return {
name: this.name,
age: this.#age, // Access private field
};
}
// Define how to restore
static [toDeserialize](value: { name: string; age: number }) {
const user = new SecureUser(value.name);
user.setAge(value.age);
return user;
}
}
const superserial = new Superserial({ classes: { SecureUser } });
const serialized = superserial.serialize(new SecureUser("Alice"));
// [["SecureUser",{"name":"Alice","age":0}]]If you are using a class from an external library and cannot modify its source code (e.g., adding decorators or
symbols), you can inject the serialization logic using defineClass.
import { Superserial } from "superserial";
// Assume this is from a 3rd-party library
class ThirdPartyUser {
constructor(public name: string, public age: number) {}
}
const superserial = new Superserial();
superserial.defineClass("ThirdPartyUser", {
type: ThirdPartyUser,
toSerialize(user: ThirdPartyUser) {
return { n: user.name, a: user.age };
},
toDeserialize(value: { n: string; a: number }) {
return new ThirdPartyUser(value.n, value.a);
},
});
const serialized = superserial.serialize(new ThirdPartyUser("Bob", 30));
console.log(serialized);
// [["ThirdPartyUser",{"n":"Bob","a":30}]]If you don't use decorators, you can simply register classes via the classes option.
const serializer = new Superserial({
classes: {
TestUser,
MyUser: User, // Register with an alias
},
});Please see benchmark results.
- SuperClosure PHP Serialize Library, superserial was inspired by this.
- flatted
- devalue
- superjson