Guides

Serialize to JavaScript

Turn a value into a JavaScript expression with serialize and serializeAsync, and read it back with deserialize.

serialize and serializeAsync return a JavaScript expression. Running the expression rebuilds the value, with the same types, shared references and cycles.

serialize

JavaScript
import { serialize } from 'seroval';

serialize({ foo: 'bar' }); // ({foo:"bar"})

serialize handles synchronous values. It cannot wait, so:

  • A promise throws a SerovalParserError, whose cause is a SerovalUnsupportedTypeError.
  • An async iterable also throws a SerovalParserError, whose cause is a SerovalUnsupportedTypeError.
  • A stream becomes an empty stream that never ends. Reading it waits forever.

Use serializeAsync or a streaming function for those values.

serializeAsync

serializeAsync waits for every promise in the value, and reads every stream and async iterable until it ends. It then returns one string.

JavaScript
import { serializeAsync } from 'seroval';

await serializeAsync(Promise.resolve({ foo: 'bar' }));
// Promise.resolve({foo:"bar"})

Warning

serializeAsync waits until every stream ends. A stream that never ends makes it wait forever. Use crossSerializeStream for long-lived streams.

deserialize

deserialize runs the expression and returns the value.

JavaScript
import { deserialize, serialize } from 'seroval';

const copy = deserialize(serialize(new Map([['a', 1]])));
copy.get('a'); // 1

deserialize is eval. Running output from an untrusted source lets that source run any code. Use the JSON functions to receive data you do not trust.

In a browser, you usually do not call deserialize. You put the output in a <script> tag instead:

JavaScript
const html = `<script>window.__DATA__=${serialize(data)}</script>`;

The output escapes </script> and similar sequences, so it is safe to embed in HTML.

Shared references

When the same object appears more than once, seroval sends it once. Every place that used it gets the same object back.

JavaScript
const parent = {};

serialize([{ parent }, { parent }]);
// (h=>[{parent:h={}},{parent:h}])()

Cyclic references

An object that refers to itself, directly or through other objects, is rebuilt with the same cycle.

JavaScript
const node = {};
node.self = node;

serialize(node);
// (h=>(h={},h.self=h,h))()

This works for cycles through several objects too:

JavaScript
const a = {};
const b = {};
a.next = b;
b.prev = a;

serialize([a, b]);
// ((h,j,k)=>(k=[h={next:j={}},j],j.prev=h,k))()

Options

Both functions take the same options:

OptionDefaultDescription
pluginsnonePlugins for types seroval does not support itself.
disabledFeatures0Features to turn off, for older runtimes.
depthLimit1000How deeply nested a value can be. Deeper values throw SerovalDepthLimitError.
compactArrayBufferViewsfalseSend only the visible bytes of typed arrays. See Binary data.

When a value cannot be serialized

A value seroval does not support, such as a function without a plugin, throws a SerovalParserError. Its cause is a SerovalUnsupportedTypeError that holds the value. See Errors.

JavaScript
try {
  serialize({ callback: () => {} });
} catch (error) {
  error.cause.value; // the function
}

To send a function, register it on both sides with createReference.