Introducing serializr: serializing and deserializing object graphs with ease
So that is where “serializr” comes in. A small library that helps decoupling your JSON data representation from your object graphs by providing a simple mechanism to serialize object graphs into JSON and vice versa.
It supports object composition, references, classes, inheritance and other common patterns. Nothing demonstrates that better than a simple example:
This listing introduces three model classes, a Drawing, Box and Arrow. A model as simple as this is already requires manual work to properly serialize it to JSON. Deserialization is even harder. Reconstructing a tree of plain object from JSON is easy, but reconstructing a graph of typed objects usually requires a lot of tedious (de)serialization code if you don’t have an opinionated ORM system in place.
For example a naive “JSON.stringify(drawing)” will emit the boxes twice in the output, one time in the boxes collection, and one time as from/to properties of the arrow:
Data relations: Containment versus association
Intuitively you will recognize that the output above is not what we desire. The reason for that is that the relation between a drawing and the boxes is a containment relation, the boxes are owned by the drawing. In contrast, the arrow doesn’t own two boxes, it is just associated with them. As a result the “to” and “from” properties should not be serialized as a complete box, but just as reference. However, these semantics cannot be inferred from just looking at the data graph, hence serializr provides the means to decorate the data model with this type of information.
So let’s alter our data model a bit. The @serializable decorator can be used to indicate that a field should be serialized, and optionally it accepts a strategy (propSchema) that expresses how a field should be serialized. The annotated model looks like this (constructors have been omitted for brevity):
These annotations are created using ESNext / TypeScript decorators. Note that this is optional, at the end of this blog post the ES5 syntax is provided as well.
It is time to meet “serializr.serialize” and “serializr.deserialize”. These functions utilize the above annotations, so serializing into a proper JSON tree and deserializing the JSON into a Drawing again is now as simple as:
The serialize functions picks up the annotations which results in, for example, the “from” property of an arrow being serialized as just the identifier value of a box instead of the complete box. And during deserialization the annotated information is used to construct objects of the proper type. The symbiosis between “identifier” and “reference” makes sure that the relations are restored after deserialization.
While “reference” may be used to restore references within a single JSON document, it is also able to resolve references to objects that have already been loaded in memory, or fetch additional data from the server. For these cases, a “lookup” function might be provided to “reference”. See the lookup function in action with this example:
Serializr provides build in strategies to serialize objects (plain or prototyped), lists, maps, primitives, references and identifiers. At some point you might encounter a pattern that doesn’t fit in the built-in strategies. Fear not, serializr provides many extension points: customizable object factories, custom property serializers, context aware deserialization… See the docs for further examples.
Serializr ain’t the next ORM library
The original reason for writing this library is that people using MobX complained that after all the boilerplate MobX eliminates, (de)serializing object graphs became one of the most cumbersome tasks. However, since this is a generic problem, and not just MobX related, serializr has been designed as a simple yet generic solution. Serializr is intended to be just a JSON (de)serialization library and not a complete ORM system. So querying data, managing your in memory data sets. That is up to you :).
Appendix: Serializr + ES5
As promised, here is a small example what using Serializr without classes or decorators look like. More extensive examples can be found in the documentation.