ig-serialize 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +180 -30
- package/package.json +1 -1
- package/serialize.js +2 -16
- package/test.js +0 -5
package/README.md
CHANGED
|
@@ -5,13 +5,12 @@ This extends the default JSON serialization adding the following:
|
|
|
5
5
|
- Sparse array serialization
|
|
6
6
|
- `undefined`/`NaN` serialization
|
|
7
7
|
- Serialization of `Infinity`, `BigInt`'s, `Set`'s, `Map`'s
|
|
8
|
-
- Function serialization
|
|
8
|
+
- Function serialization
|
|
9
9
|
- Deep and partial-deep cleen object copy
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
Data not stored:
|
|
12
|
+
- Attributes on arrays, maps, sets, and functions,
|
|
13
|
+
- Function closures.
|
|
15
14
|
|
|
16
15
|
|
|
17
16
|
## Motivation
|
|
@@ -30,16 +29,53 @@ For basic use:
|
|
|
30
29
|
$ npm install ig-serilaize
|
|
31
30
|
```
|
|
32
31
|
|
|
33
|
-
Or just download and drop [serialize.js](serialize.js) into your code.
|
|
32
|
+
Or just download and drop [serialize.js](./serialize.js) into your code.
|
|
34
33
|
|
|
35
34
|
|
|
36
35
|
```javascript
|
|
37
|
-
serialize = require('ig-serialize')
|
|
36
|
+
var serialize = require('ig-serialize')
|
|
38
37
|
```
|
|
39
38
|
|
|
40
39
|
|
|
41
40
|
## Introduction
|
|
42
41
|
|
|
42
|
+
`serialize.js` provides two toolsets:
|
|
43
|
+
|
|
44
|
+
1. A means to serialize and deserialize complex data structures into an
|
|
45
|
+
extended JSON format.
|
|
46
|
+
```javascript
|
|
47
|
+
var obj = {
|
|
48
|
+
sparse_array: [,,,,1],
|
|
49
|
+
bad_number: NaN,
|
|
50
|
+
really_large_number: 99999999999999999999999n,
|
|
51
|
+
}
|
|
52
|
+
obj.recursive = obj
|
|
53
|
+
obj.re_reference = obj.sparse_array
|
|
54
|
+
|
|
55
|
+
var str = serialize(obj)
|
|
56
|
+
|
|
57
|
+
// ...
|
|
58
|
+
|
|
59
|
+
var copy = deserialize(str)
|
|
60
|
+
```
|
|
61
|
+
This is useful when requiering serialization of data structures more
|
|
62
|
+
complex than pure JSON can handle.
|
|
63
|
+
2. A means to cleanly copy deep data structures with guaranteed isolation.
|
|
64
|
+
```javascript
|
|
65
|
+
var obj = {
|
|
66
|
+
// ...
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
var copy = deepCopy(obj)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Long strings and large `BigInt`'s
|
|
73
|
+
|
|
74
|
+
Repeating strings and `BigInt`'s longer that `MIN_LENGTH_REF` are stored
|
|
75
|
+
by reference by default.
|
|
76
|
+
|
|
77
|
+
See: [`MIN_LENGTH_REF`](#min_length_ref--optionsmin_length_ref)
|
|
78
|
+
|
|
43
79
|
|
|
44
80
|
### Serializing functions
|
|
45
81
|
|
|
@@ -58,6 +94,12 @@ Thus, care must be taken when serializing structures containing function.
|
|
|
58
94
|
|
|
59
95
|
## API
|
|
60
96
|
|
|
97
|
+
### `eJSON`
|
|
98
|
+
|
|
99
|
+
An `JSON`-api-compatible object providing [`.stringify(..)`](#serialize--ejsonstringify) and [`.parse(..)`](#deserialize--ejsonparse)
|
|
100
|
+
static methods.
|
|
101
|
+
|
|
102
|
+
|
|
61
103
|
### `serialize(..)` / `eJSON.stringify(..)`
|
|
62
104
|
|
|
63
105
|
Serialize a JavaScript value into a JSON/eJSON string.
|
|
@@ -147,23 +189,38 @@ eJSON.parse(<string>, {functions: <functions>})
|
|
|
147
189
|
|
|
148
190
|
### `deepCopy(..)`
|
|
149
191
|
|
|
192
|
+
Deep-copy an object.
|
|
193
|
+
|
|
150
194
|
```
|
|
151
195
|
deepCopy(<value>)
|
|
152
196
|
-> <value>
|
|
153
197
|
```
|
|
154
198
|
|
|
199
|
+
The returned object is a fully sanitized through serialization clean copy.
|
|
200
|
+
|
|
155
201
|
|
|
156
202
|
### `partialDeepCopy(..)`
|
|
157
203
|
|
|
204
|
+
Partially deep-copy and object, retaining only references to functions.
|
|
205
|
+
|
|
158
206
|
```
|
|
159
207
|
partialDeepCopy(<value>)
|
|
160
208
|
-> <value>
|
|
161
209
|
```
|
|
162
210
|
|
|
211
|
+
The returned object is a partially sanitized through serialization clean
|
|
212
|
+
copy with function references copied as-is from the input retaining and
|
|
213
|
+
"transferring" all associated function state like attributes and closures.
|
|
214
|
+
|
|
215
|
+
Note that this is by definition a _controlled state leak_ from input
|
|
216
|
+
object to copy, so care must be taken to _control_ object-specific state
|
|
217
|
+
in function closures and attributes -- keeping function state independent
|
|
218
|
+
from object state is _in general_ a good practice.
|
|
219
|
+
|
|
163
220
|
|
|
164
221
|
### `MIN_LENGTH_REF` / `<options>.min_length_ref`
|
|
165
222
|
|
|
166
|
-
Defines the default minimum length of repeating string or
|
|
223
|
+
Defines the default minimum length of repeating string or `BigInt`'s to
|
|
167
224
|
include as a reference in the output.
|
|
168
225
|
|
|
169
226
|
If set to `0`, referencing will be disabled.
|
|
@@ -171,9 +228,6 @@ If set to `0`, referencing will be disabled.
|
|
|
171
228
|
Default: 96
|
|
172
229
|
|
|
173
230
|
|
|
174
|
-
### `DEBUG`
|
|
175
|
-
|
|
176
|
-
|
|
177
231
|
|
|
178
232
|
## Format
|
|
179
233
|
|
|
@@ -186,32 +240,34 @@ while the input format is a bit more relaxed than in several details.
|
|
|
186
240
|
Paths are used for internal references in cases when objects are
|
|
187
241
|
encountered multiple times, e.g. in recursion.
|
|
188
242
|
|
|
189
|
-
A path is an array of keys, the
|
|
243
|
+
A path is an array of keys, the semantics of each key depend on the data
|
|
190
244
|
structure traversed:
|
|
191
|
-
-
|
|
192
|
-
-
|
|
193
|
-
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
Note that string path items are unambiguous and are always treated as
|
|
198
|
-
attributes.
|
|
245
|
+
- `Array` expects a number
|
|
246
|
+
- `Set` expects a number -- item order in set
|
|
247
|
+
- `Map` expects pair of consecutive numbers -- the first indicates item
|
|
248
|
+
order the second if `0` selects the key, if `1` selects the value.
|
|
249
|
+
- `Object` expects a string
|
|
199
250
|
|
|
200
251
|
An empty path indicates the root object.
|
|
201
252
|
|
|
202
253
|
|
|
254
|
+
Notes:
|
|
255
|
+
- String path items are unambiguous and are always treated as attributes.
|
|
256
|
+
This enables referencing of attributes of any object like arrays, maps, ...etc.
|
|
257
|
+
- Map/set paths are structured as if sets and maps are represented by
|
|
258
|
+
arrays structured as their respective constructors expect as input.
|
|
259
|
+
|
|
260
|
+
|
|
203
261
|
### Referencing
|
|
204
262
|
|
|
205
263
|
If an object is encountered for a second time it will be serialized as
|
|
206
264
|
a reference by path to the first occurrence.
|
|
207
265
|
|
|
208
|
-
|
|
266
|
+
Grammar:
|
|
209
267
|
```bnf
|
|
210
268
|
<ref> ::=
|
|
211
|
-
'<REF
|
|
212
|
-
|
|
213
|
-
<path> ::=
|
|
214
|
-
'[' <path-items> ']'
|
|
269
|
+
'<REF[]>'
|
|
270
|
+
| '<REF[' <path-items> ']>
|
|
215
271
|
|
|
216
272
|
<path-items> ::=
|
|
217
273
|
<item>
|
|
@@ -260,7 +316,7 @@ serialize([null, undefined, NaN]) // -> '[null,undefined,NaN]'
|
|
|
260
316
|
### Sparse arrays
|
|
261
317
|
|
|
262
318
|
Sparse arrays are represented in the same way JavaScript handles them
|
|
263
|
-
syntactically.
|
|
319
|
+
syntactically -- with commas separating empty "positions".
|
|
264
320
|
|
|
265
321
|
Example:
|
|
266
322
|
```javascript
|
|
@@ -268,29 +324,111 @@ serialize([,]) // -> '[,]'
|
|
|
268
324
|
serialize([,,]) // -> '[,,]'
|
|
269
325
|
```
|
|
270
326
|
|
|
271
|
-
|
|
272
|
-
them:
|
|
327
|
+
Trailing commas are handled in the same way JavaScript handles them:
|
|
273
328
|
```javascript
|
|
274
|
-
// trailing
|
|
329
|
+
// trailing commas are ignored...
|
|
275
330
|
serialize([1,]) // -> '[1]'
|
|
276
331
|
|
|
277
332
|
// sparse element...
|
|
278
333
|
serialize([1,,]) // -> '[1,,]'
|
|
279
334
|
```
|
|
280
335
|
|
|
336
|
+
|
|
281
337
|
### BigInt
|
|
282
338
|
|
|
339
|
+
Serialized as represented in JavaScript.
|
|
340
|
+
|
|
341
|
+
```javascript
|
|
342
|
+
serialize(9999999999n) // -> '9999999999n'
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
|
|
283
346
|
### Infinity
|
|
284
347
|
|
|
348
|
+
Serialized as represented in JavaScript
|
|
349
|
+
|
|
350
|
+
```javascript
|
|
351
|
+
serialize(Infinity) // -> 'Infinity'
|
|
352
|
+
serialize(-Infinity) // -> '-Infinity'
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
|
|
285
356
|
### Map / Set
|
|
286
357
|
|
|
358
|
+
Maps and sets are stored in the same format as their respective constructor
|
|
359
|
+
calls, dropping the `new` keyword:
|
|
360
|
+
|
|
361
|
+
Grammar:
|
|
362
|
+
```bnf
|
|
363
|
+
<map> ::=
|
|
364
|
+
'Map([])
|
|
365
|
+
| 'Map([' <map-items> '])
|
|
366
|
+
|
|
367
|
+
<map-items> ::=
|
|
368
|
+
<map-item>
|
|
369
|
+
| <map-item> ',' <map-items>
|
|
370
|
+
|
|
371
|
+
<map-item> ::=
|
|
372
|
+
'[' <value> ',' <value> ']'
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
```bnf
|
|
376
|
+
<set> ::=
|
|
377
|
+
'Set([])
|
|
378
|
+
| 'Set([' <set-items> '])
|
|
379
|
+
|
|
380
|
+
<set-items> ::=
|
|
381
|
+
<value>
|
|
382
|
+
| <value> ',' <set-items>
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Examples:
|
|
386
|
+
```javascript
|
|
387
|
+
serialize(new Set([1,2,3])) // -> 'Set([1,2,3])'
|
|
388
|
+
serialize(new Map([['a', 1], ['b', 2]])) // -> 'Map([["a",1],["b",2]])'
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
|
|
287
392
|
### Functions
|
|
288
393
|
|
|
394
|
+
A function can be stored in one of two ways:
|
|
395
|
+
1. As code (default)
|
|
396
|
+
2. As a function index, if an array to store function references is given.
|
|
397
|
+
|
|
398
|
+
Grammar:
|
|
399
|
+
```bnf
|
|
400
|
+
<func> ::=
|
|
401
|
+
'<FUNC[' <length> ',(' <func-spec> ')]>
|
|
402
|
+
|
|
403
|
+
<func-spec> ::=
|
|
404
|
+
<code>
|
|
405
|
+
| <index>
|
|
406
|
+
|
|
407
|
+
<length> ::= <number>
|
|
408
|
+
|
|
409
|
+
<index> ::= <number>
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
`<length>` is the length of the next block in chars, including the braces.
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
```javascript
|
|
416
|
+
serialize(function(){}) // -> '<FUNC[14,(function(){})]>'
|
|
417
|
+
|
|
418
|
+
var functions = []
|
|
419
|
+
serialize(function(){}, {functions}) // -> '<FUNC[3,(0)]>'
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
Note that deserializing functions is disabled by default as it can pose
|
|
423
|
+
a security risk if the input to `deserialize(..)` is not trusted.
|
|
424
|
+
(see: [`deserialize(..)`](#deserialize--ejsonparse))
|
|
289
425
|
|
|
290
426
|
|
|
291
427
|
|
|
292
428
|
## Running tests
|
|
293
429
|
|
|
430
|
+
`serialize.js` uses ['test.js'](/flynx/test.js) for testing.
|
|
431
|
+
|
|
294
432
|
Get the development dependencies:
|
|
295
433
|
```shell
|
|
296
434
|
$ npm install -D
|
|
@@ -303,10 +441,22 @@ $ npm test
|
|
|
303
441
|
|
|
304
442
|
To run the tests directly:
|
|
305
443
|
```shell
|
|
306
|
-
$
|
|
444
|
+
$ ./test.js
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
To run the tests with modifier chains of length 3:
|
|
448
|
+
```shell
|
|
449
|
+
$ ./test.js -m 3
|
|
307
450
|
```
|
|
308
451
|
|
|
309
452
|
|
|
310
453
|
|
|
454
|
+
## License
|
|
455
|
+
|
|
456
|
+
[BSD 3-Clause License](./LICENSE)
|
|
457
|
+
|
|
458
|
+
Copyright (c) 2014-2026, Alex A. Naanou,
|
|
459
|
+
All rights reserved.
|
|
460
|
+
|
|
311
461
|
|
|
312
462
|
<!-- vim:set nowrap : -->
|
package/package.json
CHANGED
package/serialize.js
CHANGED
|
@@ -54,7 +54,6 @@
|
|
|
54
54
|
/*********************************************************************/
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
var EMPTY = '<empty>'
|
|
58
57
|
var NULL = 'null'
|
|
59
58
|
var UNDEFINED = 'undefined'
|
|
60
59
|
var NAN = 'NaN'
|
|
@@ -334,7 +333,6 @@ module.eJSON = {
|
|
|
334
333
|
undefined: undefined,
|
|
335
334
|
NaN: NaN,
|
|
336
335
|
|
|
337
|
-
'<empty>': 'empty',
|
|
338
336
|
'<REF': 'reference',
|
|
339
337
|
|
|
340
338
|
'<FUNC[': 'func',
|
|
@@ -558,15 +556,6 @@ module.eJSON = {
|
|
|
558
556
|
initial instanceof Array
|
|
559
557
|
&& initial.length++
|
|
560
558
|
continue }
|
|
561
|
-
if(str.slice(i, i+EMPTY.length) == EMPTY){
|
|
562
|
-
index++
|
|
563
|
-
i += EMPTY.length
|
|
564
|
-
if(str[i] == ','){
|
|
565
|
-
i++ }
|
|
566
|
-
// XXX this feels hackish -- can this be deligated to the handler???
|
|
567
|
-
initial instanceof Array
|
|
568
|
-
&& initial.length++
|
|
569
|
-
continue }
|
|
570
559
|
|
|
571
560
|
// end of input...
|
|
572
561
|
if(i >= str.length-1){
|
|
@@ -782,7 +771,7 @@ function(str, options){
|
|
|
782
771
|
|
|
783
772
|
var deepCopy =
|
|
784
773
|
module.deepCopy =
|
|
785
|
-
function(obj, funcs){
|
|
774
|
+
function(obj, funcs=true){
|
|
786
775
|
var options = {functions: funcs}
|
|
787
776
|
return deserialize(
|
|
788
777
|
serialize(obj, null, 0, options),
|
|
@@ -792,10 +781,7 @@ function(obj, funcs){
|
|
|
792
781
|
var partialDeepCopy =
|
|
793
782
|
module.partialDeepCopy =
|
|
794
783
|
function(obj, funcs=[]){
|
|
795
|
-
|
|
796
|
-
return deserialize(
|
|
797
|
-
serialize(obj, null, 0, options),
|
|
798
|
-
options) }
|
|
784
|
+
return deepCopy(obj, funcs) }
|
|
799
785
|
|
|
800
786
|
|
|
801
787
|
|
package/test.js
CHANGED
|
@@ -228,11 +228,6 @@ test.Cases({
|
|
|
228
228
|
|
|
229
229
|
// arrays...
|
|
230
230
|
['[1,2,]', '[1,2]'],
|
|
231
|
-
|
|
232
|
-
// sparse arrays...
|
|
233
|
-
['[,]', '[<empty>]'],
|
|
234
|
-
['[1,2,,]', '[1,2,<empty>]'],
|
|
235
|
-
['[1,2,<empty>,]', '[1,2,<empty>]'],
|
|
236
231
|
],
|
|
237
232
|
'syntax-simplifications': function(assert){
|
|
238
233
|
var aa, bb
|