superserial
A comprehensive Serializer/Deserializer that can handle any data type.
Usage
with Deno
import { Serializer } from "https://deno.land/x/superserial/mod.ts";
const serializer = new Serializer();
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 = serializer.serialize(nodes);
console.log(serialized);
// [$1,$2];{"self":$1,"siblings":$0};{"self":$2,"siblings":$0}
with Node.js & Browser
Install
npm install superserial
import { Serializer } from "superserial";
// Usage is as above :-)
Index
Built-in Objects
Value Properties
NaN
Infinity
,-Infinity
undefined
serializer.serialize({
und: undefined,
nan: NaN,
inf: Infinity,
ninf: -Infinity,
}); // {"und":undefined,"nan":NaN,"inf":Infinity,"ninf":-Infinity}
Fundamental Objects
Symbol
ETC
BigInt
Date
RegExp
Map
Set
const symbol = Symbol();
serializer.serialize({
sym: symbol,
bigint: 100n,
date: new Date(),
regex: /abc/gmi,
map: new Map([["key1", "value1"], ["key2", "value2"]]),
set: new Set([1, 2, 3, 4]),
});
// {"sym":$1,"bigint":100n,"date":$2,"regex":$3,"map":$4,"set":$5};Symbol();Date(1648740167514);/abc/gim;Map("key1"=>"value1","key2"=>"value2");Set(1,2,3,4)
Circular Reference
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 = serializer.serialize(nodes);
console.log(serialized);
// [$1,$2];{"self":$1,"siblings":$0};{"self":$2,"siblings":$0}
const deserialized = serializer.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); // true
Circular Set & Map
const set = new Set();
set.add(set);
serializer.serialize(set); // Set($0)
const map = new Map();
map.set(map, map);
serializer.serialize(map); // Map($0=>$0)
Deserialization also works perfectly!
const set = serializer.deserialize("Set($0)");
console.log(set === [...set][0]); // true
const map = serializer.deserialize("Map($0=>$0)");
console.log(map === [...map.keys()][0]); // true
console.log(map === map.get([...map.keys()][0])); // true
Class Support
Classes contain methods, getters, etc., but JSON doesn’t fully support them. superserial includes features that make it easy to use.
The class to be used for deserialize
is defined when the Serializer is
created.
class TestUser {
constructor(
public name?: string,
public age?: number,
) {
}
}
const serializer = new Serializer({ classes: { TestUser } });
Serializes the object and then deserializes it again. Since the original class object is converted as it is, all getters and methods can be used as they are.
const serialized = serializer.serialize(new TestUser("wan2land", 20));
console.log(serialized);
// TestUser{"name":"wan2land","age":20}
const user = serializer.deserialize(serialized);
console.log(user); // TestUser { name: "wan2land", age: 20 }
toSerialize / toDeserialize
Private variables can be converted using two special symbols (toSerialize
,
toDeserialize
).
When serializing(serialize
), the object’s data is created based on the
toSerialize
method. You can check the result of toSerialize
by looking at
the serialized string.
When deserializing(deserialize
), it is impossible to create an object without
a constructor call. (ref.
No backdoor to access private)
If the toDeserialize
method is included, a value can be injected through
toDeserialize
after calling the constructor.
import {
Serializer,
toDeserialize,
toSerialize,
} from "https://deno.land/x/superserial/mod.ts";
class TestUser {
#_age = 0;
constructor(public name: string) {
this.#_age = 0;
}
setAge(age: number) {
this.#_age = age;
}
getAge() {
return this.#_age;
}
[toSerialize]() {
return {
name: this.name,
age: this.#_age,
};
}
[toDeserialize](
value: {
name: string;
age: number;
},
) {
this.name = value.name;
this.#_age = value.age;
}
}
const serializer = new Serializer({ classes: { TestUser } });
{
const user = new TestUser("wan2land");
user.setAge(20);
console.log(serializer.serialize(user)); // TestUser{"name":"wan2land","age":20}
}
{
const user = serializer.deserialize<TestUser>(
'TestUser{"name":"wan2land","age":20}',
);
console.log(user); // TestUser { name: "wan2land" }
console.log(user.getAge()); // 20
}
Benchmark
Please see benchmark results.
See also
- Creating Superserial - My blog post about superserial. (Korean)
- SuperClosure PHP Serialize Library, superserial was inspired by this.
- flatted
- lave
- arson
- devalue
- superjson