Surrial
Serialize anything. Pretty surreal!
Install
Install using your favorite package manager:
npm install surrial
yarn add surrial
Usage
const { serialize, deserialize, surrial } = require('surrial');
class Person {
constructor(name, parent){
this.name = name;
this.parent = parent;
}
}
function identity(thing) { return thing; }
const stringified = serialize({
a: 1,
b: new Date(),
c: /foo/,
d: new Set([1, 2, 3]),
e: new Map([[1, 'one'], [2, 'two']]),
f: Person,
g: identity
});
const output = deserialize(stringified)
Also supports serializing instances of classes for known classes.
const p = new Person('Foo', new Person('Bar', null));
const knownClasses = [Person];
const personString = serialize(p, knownClasses);
const copy = deserialize(p, knownClasses);
An example of the surrial
tag for template literals:
const decade = [new Date(2010, 1, 1), new Date(2020, 1, 1)];
surrial`new Set(${decade})`;
You can customize the output string using the surrialize()
method (comparable to the toJSON
method for JSON.stringify
).
class Person implements Surrializable {
public age: number;
constructor(ageInMonths: number) {
this.age = Math.floor(ageInMonths / 12);
}
surrialize() {
return surrial`new Person(${this.age * 12})`;
}
}
const input = new Person(25);
const actual = serialize(input, [Person]);
const output = deserialize(actual, [Person]);
expect(output).instanceOf(Person);
expect(output).deep.eq(input);
Api
TypeScript typings are included in the library.
export function surrial(templateLiterals: TemplateStringsArray, ...values: unknown[]) {
export function serialize(thing: any, knownClasses: ClassConstructor[] = []): string {
export function deserialize(serializedThing: string, knownClasses: ClassConstructor[] = []): any;
Features
- Serializes all primitive types
- Serializes plain objects as JSON
- Support for build in types:
Date
, RegExp
, Map
, Set
and Buffer
- Support for functions and classes using their
toString()
- Support for instances of classes using
new MyClass()
syntax (see limitations). - Support for deeply nested build in types/class instances
- Has a light footprint (< 200 lines of code).
- Written in typescript (type definition included).
- Deserialize using a
deserialize
convenience method. This uses the new Function(/*...*/)
(comparable to eval
) (see limitations). - Serialize values in a template with a handy
surrial
tagged template literal. - Allow a custom serialize function using
surrialize
.
Limitations
Surrial, like any serialization library, has some limitations, but supports my personal use case.
If you need more functionality, don't hesitate to open an issue.
I'm always in for a discussion.
Circular references
Circular references are not supported.
Deserializing is no security feature (you will get hacked!)
When you call the deserialize
method, any string will be interpreted as javascript using the new Function(...)
constructor. Keep in mind that any arbitrary code will be executed in the global scope of your current javascript engine! Please don't use this library to deserialize strings coming from untrusted sources!
Class instances
Class instances are serialized using their constructor. Any additional properties are ignored.
class Person {
constructor(name){
this.name = name;
}
}
const p = new Person('foo');
p.age = 10;
serialize(p, [Person]);
Both the class
syntax and prototype
syntax (es5 syntax) are supported here.
When serializing an instance of a class, it is assumed that the constructor parameters are also properties (or attributes) of that class. If not, that parameter will be undefined.
class Person {
constructor(n, age){
this.name = n;
this.age = age;
}
}
const p = new Person('foo', 42);
serialize(p);
When serializing a class instance, only classes you specify as knownClasses
are actually serialized using new MyClass()
,
by default it would just have a JSON format.
class Person { constructor(name) { this.name = name; }}
serialize(new Person('Foo'));
serialize(new Person('Foo'), [Person]);
When deserializing a class instance, you are responsible for providing a class definition (or a class with the same name).
class Person { constructor(name) { this.name = name; }}
deserialize('new Person("Foo")');
deserialize('new Person("Foo")', [Person]);
Acknowledgements
- This library is strongly influenced by serialize-javascript.
This might be what you're looking for when you don't need the class instance serialization support.
- A library which supports circular references: circular-json
- Know the class that you're serializing to? serialize.ts might be for you. This one also looks good: cerialize