# 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() eJSON.stringify() -> ``` More control: ``` serialize(obj, options){ serialize(obj, indent, depth=0, options){ -> ``` 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() eJSON.parse() -> ``` 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(, true) eJSON.parse(, true) deserialize(, {functions: true}) eJSON.parse(, {functions: true}) -> ``` Passing a function list (generated by `serialize(, {functions: })`) for deserialization: ``` deserialize(, {functions: }) eJSON.parse(, {functions: }) -> ``` ### `deepCopy(..)` ``` deepCopy() -> ``` ### `partialDeepCopy(..)` ``` partialDeepCopy() -> ``` ### `MIN_LENGTH_REF` / `.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: - Recursion - 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. ### Recursion and internal linking 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 ::= ' '>' ::= '[' ']' ::= | ',' ::= | ``` Example: '''javascript var o = [] o.o = o // root object reference... serialize(o) // -> '[]' // array item... serialize([o]) // -> '[[]]' // 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([[]])' // map key... serialize(new Map([[o, 'value']])) // -> 'Map([[[],"value"]])' // map value... serialize(new Map([['key', o]])) // -> 'Map([["key",[]]])' ''' ### 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 ```