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

289 lines
5.8 KiB
Markdown

# 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:
```shell
$ npm install ig-serilaize
```
Or just download and drop [serialize.js](serialize.js) into your code.
```javascript
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](https://www.json.org/json-en.html),
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:
```bnf
<ref> ::=
'<REF' <path> '>'
<path> ::=
'[' <path-items> ']'
<path-items> ::=
<item>
| <item> ',' <path-items>
<item> ::=
<number>
| <string>
```
Example:
```javascript
// 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:
```shell
$ npm install -D
```
Run the tests:
```shell
$ npm test
```
To run the tests directly:
```shell
$ node ./test.js
```