npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

serialise

v1.0.2

Published

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

Downloads

74

Readme

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.