Here comes SuperJSON

Nicholas Dobie
One Dead Pixel
Published in
4 min readDec 16, 2019

--

After years of working on advanced JavaScript applications I’ve learn a lot. As my applications have grown more complex with more data needing to be shifted around and saved. I’ve found a painful truth, JSON isn’t good enough. With more complex data being used in applications, I’ve had to start writing converters to turn the data into something that can be used by JSON and then write more converters to turn the JSON back into my complex data. This has lead to some really ugly functions, that have become nightmares to maintain.

Well I decided that I could create a better solution, so I did. There were a few key things that I wanted my solution to do. One, produce valid JSON code so that other application not using the library could still parse the data. Two, support more of the built-in JavaScript classes like Date, Map, and Set. Three, allow for custom classes. The result was SuperJSON and after a few months of testing and validating I feel it is ready for sharing.

SuperJSON is released under the MIT license and the source code is available on GitHub.

Using SuperJSON

A code example of using SuperJSON to encode and decode a date.
Simple example of SuperJSON

The two main functions of SuperJSON are SuperJSON.parse and SuperJSON.stringify which align with their JSON counterparts making SuperJSON a true drop-in replacement for JSON. Out of the box it supports primitives, arrays, plain objects, Dates, Maps, Sets, and TypedArrays with more built-ins coming.

Behind the scenes, SuperJSON replaces instances that aren’t supported natively by JSON with an object using the keys "__sj_type" and "__sj_value" when stringifying. When parsing it looks for an object containing those keys and uses them to recreate a similar instance. Type informs what class to use for the recreation and the value is JSON safe data that can be used to recreate the instance.

Nested Data

A code example of nesting dates in a set and using SuperJSON to encode and decode all data.
Example showing nested data

A huge issue I have had with custom serializers and deserializers was that they were directly tied to the data structure. This made any change to the data structure require a change to the serializers and deserializers. To get around this, SuperJSON instead walks through the data structure looking at each instance in isolation. The walk method meant that SuperJSON doesn’t need to know anything about the data structure or what to do with nested data. Instead it just knows what to do with that specific instance.

Custom Classes

With ES2015 introducing classes, I’ve found my codebases taking advantage of custom classes to streamline flows. While serializing with JSON was easy by using adding a toJSON method to the custom class, deserializing has been an issue. This was why a key objective of SuperJSON was making supporting custom classes as easy as possible.

Code example showing how to register a custom class.
Registering a custom class

For a custom class all SuperJSON needs is the constructor, a serializer function, and a deserializer function. The serializer and deserializer can be written as part of class itself to make it easier to maintain. When registering a class with SuperJSON it will look for a public toJSONValue method and a static public fromJSONValue method for the serializer and deserializer respectively. The serializer only needs to return the data need to recreate the instance, the returned data will be ran through SuperJSON so there is no requirement for the data to be JSON-safe. The deserializer will receive a restored version of what was returned from the serializer and should do its best to recreate the original instance. Once you have completed the class you simply need to pass it to SuperJSON.register and your done.

Code example showing how to pass custom serializers and deserializers to register.
Using register options

In some situations, you may not be able to update the class or want to provide custom serializers or deserializers. SuperJSON.register accepts an object of options as the second argument that you can specify the name, toJSONValue method, and fromJSONValue method. You can pass as many options as needed. This is actually how SuperJSON registers the core JavaScript classes like Date behind the scenes. By default SuperJSON will try to infer the name field but this can have naming conflicts or could be clobbered by obfuscators like UglifyJS or Terser. In these cases you can manually set the name to prevent issues.

What’s next for SuperJSON?

I’ll be looking to add more core JavaScript classes into SuperJSON and build out some utility libraries to make using SuperJSON even easier. You can request features or report bugs on the GitHub issues page. Longer term I am looking into porting SuperJSON to other languages like Python and Java to allow for more server integration options.

--

--