serialize.js/README.md
Alex A. Naanou d9b1bce502 fixes...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
2026-01-17 14:47:48 +03:00

5.8 KiB

serilize.js: Extended JSON serilization

This extends the default JSON serialization adding the following:

  • Recursive data structure serialization
  • undefined/NaN serialization
  • Serialization of BigInt's, Set's, Map's
  • Function serialization (off by default)
  • Deep and partial-deep cleen object copy

Possible differences to JSON output:

  • Repeating long strings and BigInts are referenced by default instead of being reincluded in the output.

Motivation

This was originally built as a companion to a testing module for a programming class, illustrating several concepts, including: guaranteed clean isolation of data structures via serialization, instrumenting code and tooling design, basic parsing, among others.

Installation

For basic use:

$ npm install ig-serilaize

Or just download and drop serialize.js into your code.

serialize = require('ig-serialize')

Introduction

Serializing functions

Due to how JavaScript is designed it is not possible to trivially and fully clone a function with all of it's references, .serilaize(..) will not attempt to clone any state a function may have, this will lead to loosing:

  • Function closure
  • Attributes set on the function or any of it's prototypes, including the .__proto__ value if it was changed.

Thus, care must be taken when serializing structures containing function.

API

serialize(..) / eJSON.stringify(..)

Serialize a JavaScript value into a JSON/eJSON string.

serialize(<value>)
eJSON.stringify(<value>)
	-> <string>

More control:

serialize(obj, options){
serialize(obj, indent, depth=0, options){
	-> <string>

Options format:

{
	// pretty-printing indent...
	// (default: undefined)
	indent: undefined,
	
	// outout root indent...
	// (default: 0)
	depth: 0,
	
	// minimal referenced string/bigint length...
	// (default: MIN_LENGTH_REF)
	min_length_ref: MIN_LENGTH_REF,
	
	// functions list...
	// (default: undefined)
	functions: undefined,
}

Supported options:

  • indent controls formatting and nested value indent, if set to a number that number of spaces will be used to indent nested values if given a string that string is used for indenting, note that only whitespace is supported currently. Default: undefined (disabled)
  • depth if given is a number of indent's, used to set top level indent depth of the returned string, this can be useful when pretty-printing or nesting the output. Default: 0
  • min_length_ref sets the minimal length of a string or big-int value for referencing when encountered repeatedly. If set to 0 or Infinity referencing of strings and big-ints will be is disabled. Default: 'MIN_LENGTH_REF'
  • functions if passed an array, encounterd functions will be pushed to it and stored in the output by index. Default: undefined

deserialize(..) / eJSON.parse(..)

Deserialize a JSON/eJSON into a value.

deserialize(<string>)
eJSON.parse(<string>)
	-> <value>

Deserializing function is disabled by default as it can be a security risk if the eJSON came from an untrusted source.

Enable function deserialization:

deserialize(<string>, true)
eJSON.parse(<string>, true)
deserialize(<string>, {functions: true})
eJSON.parse(<string>, {functions: true})
	-> <value>

Passing a function list (generated by serialize(<value>, {functions: <functions>})) for deserialization:

deserialize(<string>, {functions: <functions>})
eJSON.parse(<string>, {functions: <functions>})
	-> <value>

deepCopy(..)

deepCopy(<value>)
	-> <value>

partialDeepCopy(..)

partialDeepCopy(<value>)
	-> <value>

MIN_LENGTH_REF / <options>.min_length_ref

Defines the default minimum length of repeating string or bin-int to include as a reference in the output.

If set to 0, referencing will be disabled.

Default: 96

DEBUG

Format

The output of .serialize(..) is a strict superset of standard JSON, while the input format is a bit more relaxed than in several details.

Extensions to JSON:

  • References
  • undefined / NaN
  • BigInt
  • Map / Set
  • Function

Structural paths

Paths are used for internal references in cases when objects are encountered multiple times, e.g. in recursion.

A path is an array of keys, the meaning of each key depends on the data structure traversed:

  • array -> number
  • object -> string
  • set -> number -- item order in set
  • map -> pair of numbers -- the first indicates item order the second if 0 selects the key, if 1 selects the value.

Note that string path items are unambiguous and are always treated as attributes.

For examples see next section.

Referencing

If an object is encountered for a second time it will be serialized as a reference by path to the first occurrence.

An empty path indicates the root object.

Format:

<ref> ::= 
	'<REF' <path> '>'
	
<path> ::= 
	'[' <path-items> ']'
	
<path-items> ::=
	<item>
	| <item> ',' <path-items>
	
<item> ::=
	<number>
	| <string> 

Example:

// a recursive array...
var o = []
o.o = o

// root object reference...
serialize(o) // -> '[<REF[]>]'

// array item...
serialize([o]) // -> '[[<REF[0]>]]'

// set item...
// NOTE: the path here is the same as in the above example -- since we 
//	use ordered topology for paths sets do not differ from arrays.
serialize(new Set([o])) // -> 'Set([[<REF[0]>]])'

// map key...
serialize(new Map([[o, 'value']])) // -> 'Map([[[<REF[0,0]>],"value"]])'

// map value...
serialize(new Map([['key', o]])) // -> 'Map([["key",[<REF[0,1]>]]])'

null types

BigInt

Map / Set

Functions

Running tests

Get the development dependencies:

$ npm install -D

Run the tests:

$ npm test

To run the tests directly:

$ node ./test.js