2026-01-01 12:57:24 +03:00
|
|
|
# serilize.js: Extended JSON serilization
|
|
|
|
|
|
|
|
|
|
This extends the default JSON serialization adding the following:
|
|
|
|
|
- Recursive data structure serialization
|
2026-01-17 15:21:47 +03:00
|
|
|
- Sparse array serialization
|
2026-01-01 12:57:24 +03:00
|
|
|
- `undefined`/`NaN` serialization
|
2026-01-17 15:21:47 +03:00
|
|
|
- Serialization of `Infinity`, `BigInt`'s, `Set`'s, `Map`'s
|
2026-01-01 12:57:24 +03:00
|
|
|
- Function serialization (off by default)
|
|
|
|
|
- Deep and partial-deep cleen object copy
|
|
|
|
|
|
2026-01-14 18:18:06 +03:00
|
|
|
Possible differences to JSON output:
|
2026-01-16 16:21:59 +03:00
|
|
|
- Repeating long strings and BigInts are referenced by default instead of
|
|
|
|
|
being reincluded in the output.
|
|
|
|
|
|
2026-01-01 12:57:24 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
## Motivation
|
|
|
|
|
|
2026-01-01 23:56:20 +03:00
|
|
|
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
|
|
|
|
|
|
2026-01-17 03:14:24 +03:00
|
|
|
For basic use:
|
2026-01-14 18:04:57 +03:00
|
|
|
```shell
|
|
|
|
|
$ npm install ig-serilaize
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Or just download and drop [serialize.js](serialize.js) into your code.
|
|
|
|
|
|
|
|
|
|
|
2026-01-17 03:14:24 +03:00
|
|
|
```javascript
|
|
|
|
|
serialize = require('ig-serialize')
|
|
|
|
|
```
|
|
|
|
|
|
2026-01-01 23:56:20 +03:00
|
|
|
|
|
|
|
|
## 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.
|
|
|
|
|
|
|
|
|
|
|
2026-01-14 18:18:06 +03:00
|
|
|
|
2026-01-01 23:56:20 +03:00
|
|
|
## API
|
|
|
|
|
|
2026-01-14 18:28:56 +03:00
|
|
|
### `serialize(..)` / `eJSON.stringify(..)`
|
2026-01-14 18:18:06 +03:00
|
|
|
|
2026-01-15 15:24:05 +03:00
|
|
|
Serialize a JavaScript value into a JSON/eJSON string.
|
2026-01-15 15:07:37 +03:00
|
|
|
```
|
|
|
|
|
serialize(<value>)
|
|
|
|
|
eJSON.stringify(<value>)
|
|
|
|
|
-> <string>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
More control:
|
|
|
|
|
```
|
|
|
|
|
serialize(obj, options){
|
|
|
|
|
serialize(obj, indent, depth=0, options){
|
|
|
|
|
-> <string>
|
|
|
|
|
```
|
|
|
|
|
|
2026-01-16 16:21:59 +03:00
|
|
|
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,
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2026-01-15 15:07:37 +03:00
|
|
|
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`
|
|
|
|
|
|
|
|
|
|
|
2026-01-15 15:39:51 +03:00
|
|
|
### `deserialize(..)` / `eJSON.parse(..)`
|
2026-01-14 18:18:06 +03:00
|
|
|
|
2026-01-15 15:24:05 +03:00
|
|
|
Deserialize a JSON/eJSON into a value.
|
2026-01-15 15:07:37 +03:00
|
|
|
```
|
|
|
|
|
deserialize(<string>)
|
|
|
|
|
eJSON.parse(<string>)
|
|
|
|
|
-> <value>
|
|
|
|
|
```
|
|
|
|
|
|
2026-01-15 15:24:05 +03:00
|
|
|
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>
|
|
|
|
|
```
|
|
|
|
|
|
2026-01-15 15:07:37 +03:00
|
|
|
|
2026-01-14 18:28:56 +03:00
|
|
|
### `deepCopy(..)`
|
2026-01-14 18:18:06 +03:00
|
|
|
|
2026-01-15 15:07:37 +03:00
|
|
|
```
|
|
|
|
|
deepCopy(<value>)
|
|
|
|
|
-> <value>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
2026-01-14 18:28:56 +03:00
|
|
|
### `partialDeepCopy(..)`
|
2026-01-14 18:18:06 +03:00
|
|
|
|
2026-01-15 15:07:37 +03:00
|
|
|
```
|
|
|
|
|
partialDeepCopy(<value>)
|
|
|
|
|
-> <value>
|
|
|
|
|
```
|
|
|
|
|
|
2026-01-14 18:18:06 +03:00
|
|
|
|
2026-01-14 18:28:56 +03:00
|
|
|
### `MIN_LENGTH_REF` / `<options>.min_length_ref`
|
2026-01-14 18:18:06 +03:00
|
|
|
|
2026-01-14 18:28:56 +03:00
|
|
|
Defines the default minimum length of repeating string or bin-int to
|
|
|
|
|
include as a reference in the output.
|
2026-01-01 23:56:20 +03:00
|
|
|
|
2026-01-14 18:28:56 +03:00
|
|
|
If set to `0`, referencing will be disabled.
|
2026-01-01 23:56:20 +03:00
|
|
|
|
2026-01-14 18:28:56 +03:00
|
|
|
Default: 96
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### `DEBUG`
|
2026-01-01 23:56:20 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-01-09 15:39:24 +03:00
|
|
|
## Format
|
|
|
|
|
|
2026-01-11 03:51:26 +03:00
|
|
|
The output of `.serialize(..)` is a strict superset of [standard JSON](https://www.json.org/json-en.html),
|
2026-01-10 03:44:59 +03:00
|
|
|
while the input format is a bit more relaxed than in several details.
|
2026-01-09 15:39:24 +03:00
|
|
|
|
2026-01-10 03:44:59 +03:00
|
|
|
|
|
|
|
|
### Structural paths
|
|
|
|
|
|
2026-01-17 14:43:29 +03:00
|
|
|
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.
|
|
|
|
|
|
2026-01-17 15:21:47 +03:00
|
|
|
An empty path indicates the root object.
|
2026-01-17 14:43:29 +03:00
|
|
|
|
|
|
|
|
|
2026-01-17 14:47:48 +03:00
|
|
|
### Referencing
|
2026-01-10 03:44:59 +03:00
|
|
|
|
2026-01-17 14:43:29 +03:00
|
|
|
If an object is encountered for a second time it will be serialized as
|
|
|
|
|
a reference by path to the first occurrence.
|
|
|
|
|
|
|
|
|
|
Format:
|
|
|
|
|
```bnf
|
|
|
|
|
<ref> ::=
|
|
|
|
|
'<REF' <path> '>'
|
|
|
|
|
|
|
|
|
|
<path> ::=
|
|
|
|
|
'[' <path-items> ']'
|
|
|
|
|
|
|
|
|
|
<path-items> ::=
|
|
|
|
|
<item>
|
|
|
|
|
| <item> ',' <path-items>
|
|
|
|
|
|
|
|
|
|
<item> ::=
|
|
|
|
|
<number>
|
|
|
|
|
| <string>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Example:
|
2026-01-17 14:47:48 +03:00
|
|
|
```javascript
|
|
|
|
|
// a recursive array...
|
2026-01-17 14:43:29 +03:00
|
|
|
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]>]]])'
|
2026-01-17 14:47:48 +03:00
|
|
|
```
|
2026-01-17 14:43:29 +03:00
|
|
|
|
2026-01-13 03:20:11 +03:00
|
|
|
|
2026-01-17 15:21:47 +03:00
|
|
|
### Null types
|
|
|
|
|
|
|
|
|
|
In addition to `null`, serialize.js adds support for `undefined` and
|
|
|
|
|
`NaN` which are stored as-is.
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
```javascript
|
|
|
|
|
serialize([null, undefined, NaN]) // -> '[null,undefined,NaN]'
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Sparse arrays
|
|
|
|
|
|
|
|
|
|
Sparse arrays are represented in the same way JavaScript handles them
|
|
|
|
|
syntactically.
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
```javascript
|
|
|
|
|
serialize([,]) // -> '[,]'
|
|
|
|
|
serialize([,,]) // -> '[,,]'
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Note that trailing commas are ignored in the same way JavaScript ignores
|
|
|
|
|
them:
|
|
|
|
|
```javascript
|
|
|
|
|
// trailing comma...
|
|
|
|
|
serialize([1,]) // -> '[1]'
|
|
|
|
|
|
|
|
|
|
// sparse element...
|
|
|
|
|
serialize([1,,]) // -> '[1,,]'
|
|
|
|
|
```
|
2026-01-10 03:44:59 +03:00
|
|
|
|
|
|
|
|
### BigInt
|
|
|
|
|
|
2026-01-17 15:21:47 +03:00
|
|
|
### Infinity
|
|
|
|
|
|
2026-01-10 03:44:59 +03:00
|
|
|
### Map / Set
|
|
|
|
|
|
|
|
|
|
### Functions
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-01-17 03:14:24 +03:00
|
|
|
## Running tests
|
|
|
|
|
|
|
|
|
|
Get the development dependencies:
|
|
|
|
|
```shell
|
|
|
|
|
$ npm install -D
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Run the tests:
|
|
|
|
|
```shell
|
|
|
|
|
$ npm test
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
To run the tests directly:
|
|
|
|
|
```shell
|
|
|
|
|
$ node ./test.js
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
2026-01-10 03:44:59 +03:00
|
|
|
|
2026-01-01 12:57:24 +03:00
|
|
|
|
2026-01-17 15:21:47 +03:00
|
|
|
<!-- vim:set nowrap : -->
|