New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

serialise

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

serialise

Convert objects and class instances to/from JSON.stringify-able object literals.

latest
Source
npmnpm
Version
1.0.2
Version published
Weekly downloads
9
-25%
Maintainers
1
Weekly downloads
 
Created
Source

Serialise

Convert objects and class instances to/from JSON.stringify-able object literals.

In modern Javascript and Typescript, we normally follow the pattern of having data on instances, and methods on the prototype/class:


class MyObject {
	constructor() {
		this.a = 42;
	}

	method() {
		return this.a + this.a;
	}
};

const myInstance = new MyObject();

serialise can be used to serialise such objects to a format with only primitives, so you can pass them to JSON.stringify happily.


const S = require('serialise');

console.log(JSON.stringify(myInstance)); // Error

console.log(JSON.stringify( S.serialise(myInstance) )); // OK

Serialised objects can be deserialised and will have their original prototypes and properties restored for you. The restored object will be identical to the original. You must register any constructors you use before deserialisation.


S.serialisable(MyObject);

const deserialised = S.deserialise(
						JSON.parse(
							JSON.stringify(
								S.serialise(myInstance) )));

assert.deepStrictEqual(deserialised, myInstance); // OK

assert.strictEqual(deserialised.method(), 84); // OK

This works by only storing direct properties of the serialised object (in the example, myInstance.a), and restoring the prototype onto the deserialised object. Prototype restoration is done without mutation, so performance of deserialised objects should be identical to that with the original objects.

We store full property information (writable, enumerable, etc), so your pathological classes should truly be identical after deserialisation.

We also do a fair bit of magic to ensure that classes that extend from builtins are also serialisable:


class MyStupidString extends String {
	constructor(s) {
		super(`wrapped s`);

		Object.defineProperty(this, 'wtf', {
			value: 'why would you do this?',
			enumerable: false,
			writable: false,
			configurable: false
		});
	}

	method() { return this.split('').join('-') }
}

const myString = new MyStupidString('hello');
const deserialised = S.deserialise(S.serialise(myInstance));

assert.deepStrictEqual(deserialised, myString); // OK

assert.strictEqual(deserialised.method(), 'w-r-a-p-p-e-d- -h-e-l-l-o'); // OK

assert.strictEqual(deserialised.toString(), 'wrapped hello'); // OK

Other serialisation libraries (e.g. the otherwise excellent serialijse) fail miserably on the above two intricacies, which is what lead me to write this.

If you are not using classes, classical prototype equivalents also work, as long as you are suitably anal about setting a constructor:

function MyObject() {
	this.a = 42;
}
MyObject.prototype = Object.create(Object.prototype);
MyObject.prototype.constructor = MyObject;
MyObject.prototype.method = function() {
	return this.a + this.a;
}

S.serialisable(MyObject);

const deserialised = S.deserialise(S.serialise(myInstance) )));

assert.deepStrictEqual(deserialised, myInstance); // OK

assert.strictEqual(deserialised.method(), 84); // OK

Typescript is also supported (indeed, this module is written in Typescript):


@S.serialisable
class MyObject {
	a: number;
	constructor() {
		this.a = 42;
	}

	method() {
		return this.a + this.a;
	}
};

const deserialised = S.deserialise<MyObject>(S.serialise(myInstance) )));

assert.deepStrictEqual(deserialised, myInstance); // OK

assert.strictEqual(deserialised.method(), 84); // OK

Caveats

  • Functions are not serialised themselves, you should define them on prototypes instead of literals. Even theoretically, is only partially possible to serialise functions, as their code is accessible but not their closure context. So, for example, given the following code,
function buildDoubler(x) {
	return function() {
		return x * 2;
	}
}

const myDoubler = buildDoubler(5);

as far as I am aware it is impossible (assuming I'm not about to write a C++ Node hack) for me to properly serialise the function myDoubler.

  • Serialisation of types inheriting native types (class SuperString extends String {}) only works on ES6 implementations, sorry.

API

serialise(object)

serialise(object: any): any

Serialises an object. You can pass the result of this to JSON.stringify safely.

deserialise(serialised)

deserialise(serialised: any): any
deserialise<T>(serialised: any): T

Deserialises a previously serialised object. You must register any constructors your object or its properties are built with before you call this (assuming you have used them; you can [de]serialise literals and primitives without hassle).

serialisable(Constructor)

serialisable(MyObject: Function): void
@serialisable
class MyObject { ... }

Marks a constructor so that deserialise knows where to look to find prototypes etc.

Keywords

serialization

FAQs

Package last updated on 13 Dec 2016

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts