mirror of
https://github.com/flynx/serialize.js.git
synced 2026-07-14 04:50:57 +00:00
function support added (off by default).
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
9fa5fcbc9f
commit
1543469cdf
114
serialize2.js
114
serialize2.js
@ -17,6 +17,8 @@ var NEG_INFINITY = '-Infinity'
|
|||||||
|
|
||||||
var RECURSIVE = '<RECURSIVE%>'
|
var RECURSIVE = '<RECURSIVE%>'
|
||||||
|
|
||||||
|
var FUNCTION = '<FUNCTION[%]>'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
@ -48,7 +50,7 @@ var debug = {
|
|||||||
// string - string to use for indenting
|
// string - string to use for indenting
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// serialize(obj, base_path, seen, indent, depth)
|
// _serialize(obj, base_path, seen, indent, depth, functions)
|
||||||
// -> str
|
// -> str
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
@ -96,38 +98,31 @@ var debug = {
|
|||||||
// ]
|
// ]
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// XXX add function support...
|
|
||||||
// XXX need to destinguish between map key and value in path...
|
|
||||||
// XXX BUG: using non-whitespace as indent breaks the depth of the first
|
// XXX BUG: using non-whitespace as indent breaks the depth of the first
|
||||||
// or last elements in sequences
|
// or last elements in sequences
|
||||||
// ...breaks .trim*() in Map/Set/Object...
|
// ...breaks .trim*() in Map/Set/Object...
|
||||||
var serialize =
|
var _serialize =
|
||||||
module.serialize =
|
module._serialize =
|
||||||
function(obj, path=[], seen=new Map(), indent, depth=0){
|
function(obj, path=[], seen=new Map(), indent, depth=0, functions){
|
||||||
// args...
|
|
||||||
var args = [...arguments].slice(1)
|
|
||||||
if(typeof(args[0]) == 'number'
|
|
||||||
|| typeof(args[0]) == 'string'){
|
|
||||||
indent = args.shift() }
|
|
||||||
indent =
|
|
||||||
typeof(indent) == 'number' ?
|
|
||||||
' '.repeat(indent)
|
|
||||||
: indent
|
|
||||||
if(typeof(args[0]) == 'number'){
|
|
||||||
depth = args.shift() }
|
|
||||||
;[path, seen] = args
|
|
||||||
path ??= []
|
|
||||||
seen ??= new Map()
|
|
||||||
|
|
||||||
|
|
||||||
// recursive...
|
// recursive...
|
||||||
var p = seen.get(obj)
|
var p = seen.get(obj)
|
||||||
if(p != null){
|
if(p != null){
|
||||||
// NOTE: serialize(..) is always printed flat here, regardless of indent/depth...
|
// NOTE: _serialize(..) is always printed flat here, regardless of indent/depth...
|
||||||
return RECURSIVE.replace('%', serialize(p)) }
|
return RECURSIVE.replace('%', _serialize(p)) }
|
||||||
|
|
||||||
// XXX functions...
|
// functions...
|
||||||
// XXX
|
// NOTE: we are storing function length to avoid parsing the function...
|
||||||
|
// NOTE: storing length is a potential attack vector...
|
||||||
|
if(typeof(obj) == 'function'){
|
||||||
|
seen.set(obj, path)
|
||||||
|
// if functions array is given add function to it and store its
|
||||||
|
// index in the serialized data...
|
||||||
|
if(functions != null){
|
||||||
|
functions.push(obj)
|
||||||
|
obj = functions.length-1 }
|
||||||
|
var s = '('+ obj.toString() +')'
|
||||||
|
return FUNCTION
|
||||||
|
.replace('%', s.length +','+ s) }
|
||||||
|
|
||||||
// atomics...
|
// atomics...
|
||||||
if(obj === null){
|
if(obj === null){
|
||||||
@ -157,20 +152,20 @@ function(obj, path=[], seen=new Map(), indent, depth=0){
|
|||||||
for(var i=0; i < obj.length; i++){
|
for(var i=0; i < obj.length; i++){
|
||||||
elems.push(
|
elems.push(
|
||||||
i in obj ?
|
i in obj ?
|
||||||
serialize(obj[i], [...path, i], seen, indent, depth+1)
|
_serialize(obj[i], [...path, i], seen, indent, depth+1, functions)
|
||||||
: EMPTY) }
|
: EMPTY) }
|
||||||
} else if(obj instanceof Map){
|
} else if(obj instanceof Map){
|
||||||
pre = 'Map(['
|
pre = 'Map(['
|
||||||
post = '])'
|
post = '])'
|
||||||
elems = [
|
elems = [
|
||||||
serialize([...obj], path, seen, indent, depth)
|
_serialize([...obj], path, seen, indent, depth, functions)
|
||||||
.slice(1, -1)
|
.slice(1, -1)
|
||||||
.trim() ]
|
.trim() ]
|
||||||
} else if(obj instanceof Set){
|
} else if(obj instanceof Set){
|
||||||
pre = 'Set(['
|
pre = 'Set(['
|
||||||
post = '])'
|
post = '])'
|
||||||
elems = [
|
elems = [
|
||||||
serialize([...obj], path, seen, indent, depth)
|
_serialize([...obj], path, seen, indent, depth, functions)
|
||||||
.slice(1, -1)
|
.slice(1, -1)
|
||||||
.trim() ]
|
.trim() ]
|
||||||
} else {
|
} else {
|
||||||
@ -180,7 +175,7 @@ function(obj, path=[], seen=new Map(), indent, depth=0){
|
|||||||
elems.push(`${
|
elems.push(`${
|
||||||
JSON.stringify(k)
|
JSON.stringify(k)
|
||||||
}:${ indent != null ? ' ' : '' }${
|
}:${ indent != null ? ' ' : '' }${
|
||||||
serialize(v, [...path, k], seen, indent, depth+1)
|
_serialize(v, [...path, k], seen, indent, depth+1, functions)
|
||||||
// relevant for pretty-printing only...
|
// relevant for pretty-printing only...
|
||||||
.trimLeft()
|
.trimLeft()
|
||||||
}`) } }
|
}`) } }
|
||||||
@ -198,6 +193,11 @@ function(obj, path=[], seen=new Map(), indent, depth=0){
|
|||||||
|
|
||||||
return pre+ elems.join(join) +post }
|
return pre+ elems.join(join) +post }
|
||||||
|
|
||||||
|
// user interface...
|
||||||
|
var serialize =
|
||||||
|
module.serialize =
|
||||||
|
function(obj, indent, depth=0, functions){
|
||||||
|
return _serialize(obj, [], new Map(), indent, depth, functions) }
|
||||||
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
@ -234,6 +234,8 @@ module.eJSON = {
|
|||||||
|
|
||||||
'<empty>': 'empty',
|
'<empty>': 'empty',
|
||||||
'<RECURSIVE': 'recursive',
|
'<RECURSIVE': 'recursive',
|
||||||
|
|
||||||
|
'<FUNCTION[': 'func',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
@ -305,7 +307,7 @@ module.eJSON = {
|
|||||||
var k = path.at(-1)
|
var k = path.at(-1)
|
||||||
if(parent instanceof Set){
|
if(parent instanceof Set){
|
||||||
var elems = [...parent]
|
var elems = [...parent]
|
||||||
elems[k] = obj
|
elems[k] = value
|
||||||
// we cant to keep the order...
|
// we cant to keep the order...
|
||||||
parent.clear()
|
parent.clear()
|
||||||
for(var e of elems){
|
for(var e of elems){
|
||||||
@ -543,9 +545,36 @@ module.eJSON = {
|
|||||||
rec.push([path, obj])
|
rec.push([path, obj])
|
||||||
return [{}, i, line] }) },
|
return [{}, i, line] }) },
|
||||||
|
|
||||||
|
// XXX disable this by default...
|
||||||
|
// XXX can we avoid eval here???
|
||||||
func: function(state, path, match, str, i, line){
|
func: function(state, path, match, str, i, line){
|
||||||
// XXX
|
if(state.functions == null){
|
||||||
},
|
this.error('Deserializing functions disabled.', str, i, line) }
|
||||||
|
|
||||||
|
debug.lex('function', str, i, line)
|
||||||
|
var res
|
||||||
|
// length...
|
||||||
|
i += match.length
|
||||||
|
var [l, i, line] = this.number(state, path, str[i], str, i, line)
|
||||||
|
if(str[i] != ','){
|
||||||
|
this.error('Expected "," got "'+ str[i] +'"', str, i, line) }
|
||||||
|
i++
|
||||||
|
|
||||||
|
// func ref...
|
||||||
|
if(state.functions instanceof Array){
|
||||||
|
var [n, i, line] = this.number(state, path, str[i+1], str, i+1, line)
|
||||||
|
res = state.functions[n]
|
||||||
|
// func code...
|
||||||
|
} else {
|
||||||
|
var code = str.slice(i, i+l)
|
||||||
|
i += l
|
||||||
|
line += code.split(/\n/g).length
|
||||||
|
if(str.slice(i, i+2) == ']>'){
|
||||||
|
res = eval?.(code) } }
|
||||||
|
|
||||||
|
if(str.slice(i, i+2) != ']>'){
|
||||||
|
this.error('Expected "]>" got "'+ str.slice(i, i+2) +'"', str, i, line) }
|
||||||
|
return [res, i+2, line] },
|
||||||
|
|
||||||
value: function(state, path, str, i=0, line=0){
|
value: function(state, path, str, i=0, line=0){
|
||||||
//debug.lex('value', str, i, line)
|
//debug.lex('value', str, i, line)
|
||||||
@ -569,7 +598,7 @@ module.eJSON = {
|
|||||||
var context = 8
|
var context = 8
|
||||||
var pre = Math.max(i-context, 0)
|
var pre = Math.max(i-context, 0)
|
||||||
var post = Math.min(i+context, str.length)
|
var post = Math.min(i+context, str.length)
|
||||||
this.error(`Unexpected: ${ str[i] }`, str, i, line) }
|
this.error(`Unexpected: ${ str.slice(i, i+10) }`, str, i, line) }
|
||||||
|
|
||||||
// value...
|
// value...
|
||||||
if(typeof(handler) != 'string'){
|
if(typeof(handler) != 'string'){
|
||||||
@ -578,10 +607,10 @@ module.eJSON = {
|
|||||||
return this[handler](state, path, match, str, i, line) },
|
return this[handler](state, path, match, str, i, line) },
|
||||||
|
|
||||||
|
|
||||||
parse: function(str){
|
parse: function(str, functions){
|
||||||
|
|
||||||
// stage 1: build the object...
|
// stage 1: build the object...
|
||||||
var state = {}
|
var state = {functions}
|
||||||
var res = this.value(state, [], str)[0]
|
var res = this.value(state, [], str)[0]
|
||||||
|
|
||||||
// stage 2: link the recursive structures...
|
// stage 2: link the recursive structures...
|
||||||
@ -594,14 +623,13 @@ module.eJSON = {
|
|||||||
|
|
||||||
var deserialize =
|
var deserialize =
|
||||||
module.deserialize =
|
module.deserialize =
|
||||||
function(str){
|
function(str, functions){
|
||||||
return eJSON.parse(str) }
|
return eJSON.parse(str, functions) }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
// utils...
|
// utils...
|
||||||
// XXX see ./serialize.js
|
|
||||||
|
|
||||||
var deepCopy =
|
var deepCopy =
|
||||||
module.deepCopy =
|
module.deepCopy =
|
||||||
@ -610,6 +638,14 @@ function(obj){
|
|||||||
serialize(obj)) }
|
serialize(obj)) }
|
||||||
|
|
||||||
|
|
||||||
|
var semiDeepCopy =
|
||||||
|
module.semiDeepCopy =
|
||||||
|
function(obj){
|
||||||
|
var funcs = []
|
||||||
|
return deserialize(
|
||||||
|
serialize(obj, null, 0, funcs),
|
||||||
|
funcs) }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
|
|||||||
55
test.js
55
test.js
@ -28,6 +28,17 @@ var ejson = false
|
|||||||
|
|
||||||
// XXX test whitespace handling...
|
// XXX test whitespace handling...
|
||||||
var setups = test.Setups({
|
var setups = test.Setups({
|
||||||
|
'true': function(assert){
|
||||||
|
return ['true', json] },
|
||||||
|
'false': function(assert){
|
||||||
|
return ['false', json] },
|
||||||
|
'null': function(assert){
|
||||||
|
return ['null', json] },
|
||||||
|
'undefined': function(assert){
|
||||||
|
return ['undefined'] },
|
||||||
|
'NaN': function(assert){
|
||||||
|
return ['NaN'] },
|
||||||
|
|
||||||
number: function(assert){
|
number: function(assert){
|
||||||
return ['123', json] },
|
return ['123', json] },
|
||||||
'number-neg': function(assert){
|
'number-neg': function(assert){
|
||||||
@ -49,20 +60,10 @@ var setups = test.Setups({
|
|||||||
return ['Infinity'] },
|
return ['Infinity'] },
|
||||||
nInfinity: function(assert){
|
nInfinity: function(assert){
|
||||||
return ['-Infinity'] },
|
return ['-Infinity'] },
|
||||||
|
|
||||||
// XXX also test diffrerent quotations...
|
// XXX also test diffrerent quotations...
|
||||||
string: function(assert){
|
string: function(assert){
|
||||||
return ['"string"', json] },
|
return ['"string"', json] },
|
||||||
'true': function(assert){
|
|
||||||
return ['true', json] },
|
|
||||||
'false': function(assert){
|
|
||||||
return ['false', json] },
|
|
||||||
|
|
||||||
'null': function(assert){
|
|
||||||
return ['null', json] },
|
|
||||||
'undefined': function(assert){
|
|
||||||
return ['undefined'] },
|
|
||||||
'NaN': function(assert){
|
|
||||||
return ['NaN'] },
|
|
||||||
|
|
||||||
'array-empty': function(assert){
|
'array-empty': function(assert){
|
||||||
return ['[]', json] },
|
return ['[]', json] },
|
||||||
@ -74,8 +75,6 @@ var setups = test.Setups({
|
|||||||
return ['[<empty>,"a"]'] },
|
return ['[<empty>,"a"]'] },
|
||||||
'array-sparse-d': function(assert){
|
'array-sparse-d': function(assert){
|
||||||
return ['[<empty>,1,<empty>,<empty>,2,<empty>]'] },
|
return ['[<empty>,1,<empty>,<empty>,2,<empty>]'] },
|
||||||
'array-recursive': function(assert){
|
|
||||||
return ['[<RECURSIVE[]>]'] },
|
|
||||||
|
|
||||||
'object-empty': function(assert){
|
'object-empty': function(assert){
|
||||||
return ['{}', json] },
|
return ['{}', json] },
|
||||||
@ -86,7 +85,21 @@ var setups = test.Setups({
|
|||||||
'map-empty': function(assert){
|
'map-empty': function(assert){
|
||||||
return ['Map([])'] },
|
return ['Map([])'] },
|
||||||
|
|
||||||
// XXX
|
'function': function(assert){
|
||||||
|
return ['<FUNCTION[14,(function(){})]>'] },
|
||||||
|
|
||||||
|
// recursive...
|
||||||
|
'array-recursive': function(assert){
|
||||||
|
return ['[<RECURSIVE[]>]'] },
|
||||||
|
'object-recursive': function(assert){
|
||||||
|
return ['{"r":<RECURSIVE[]>}'] },
|
||||||
|
'set-recursive': function(assert){
|
||||||
|
return ['Set([<RECURSIVE[]>])'] },
|
||||||
|
'map-recursive-key': function(assert){
|
||||||
|
return ['Map([[<RECURSIVE[]>,"value"]])'] },
|
||||||
|
'map-recursive-value': function(assert){
|
||||||
|
return ['Map([["key",<RECURSIVE[]>]])'] },
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test.Modifiers({
|
test.Modifiers({
|
||||||
@ -95,32 +108,32 @@ test.Modifiers({
|
|||||||
'array-stuffed': function(assert, [setup, json]){
|
'array-stuffed': function(assert, [setup, json]){
|
||||||
return [
|
return [
|
||||||
eJSON.serialize(
|
eJSON.serialize(
|
||||||
[ eJSON.deserialize(setup) ] ),
|
[ eJSON.deserialize(setup, true) ] ),
|
||||||
json,
|
json,
|
||||||
] },
|
] },
|
||||||
'object-stuffed': function(assert, [setup, json]){
|
'object-stuffed': function(assert, [setup, json]){
|
||||||
return [
|
return [
|
||||||
eJSON.serialize(
|
eJSON.serialize(
|
||||||
{ key: eJSON.deserialize(setup) } ),
|
{ key: eJSON.deserialize(setup, true) } ),
|
||||||
json,
|
json,
|
||||||
] },
|
] },
|
||||||
// NOTE: these explicitly remove JSON compatibility regardless of input...
|
// NOTE: these explicitly remove JSON compatibility regardless of input...
|
||||||
'set-stuffed': function(assert, [setup]){
|
'set-stuffed': function(assert, [setup]){
|
||||||
return [
|
return [
|
||||||
eJSON.serialize(
|
eJSON.serialize(
|
||||||
new Set([ eJSON.deserialize(setup) ]) ),
|
new Set([ eJSON.deserialize(setup, true) ]) ),
|
||||||
ejson,
|
ejson,
|
||||||
] },
|
] },
|
||||||
'map-key-stuffed': function(assert, [setup]){
|
'map-key-stuffed': function(assert, [setup]){
|
||||||
return [
|
return [
|
||||||
eJSON.serialize(
|
eJSON.serialize(
|
||||||
new Map([[eJSON.deserialize(setup), "value"]]) ),
|
new Map([[eJSON.deserialize(setup, true), "value"]]) ),
|
||||||
ejson,
|
ejson,
|
||||||
] },
|
] },
|
||||||
'map-value-stuffed': function(assert, [setup]){
|
'map-value-stuffed': function(assert, [setup]){
|
||||||
return [
|
return [
|
||||||
eJSON.serialize(
|
eJSON.serialize(
|
||||||
new Map([["key", eJSON.deserialize(setup)]]) ),
|
new Map([["key", eJSON.deserialize(setup, true)]]) ),
|
||||||
ejson,
|
ejson,
|
||||||
] },
|
] },
|
||||||
})
|
})
|
||||||
@ -129,14 +142,14 @@ test.Tests({
|
|||||||
'self-apply': function(assert, [setup]){
|
'self-apply': function(assert, [setup]){
|
||||||
var res
|
var res
|
||||||
assert(
|
assert(
|
||||||
setup == (res = eJSON.serialize( eJSON.deserialize(setup) ) ),
|
setup == (res = eJSON.serialize( eJSON.deserialize(setup, true) ) ),
|
||||||
'serialize(deserialize( setup )) == setup: expected:', setup, 'got:', res) },
|
'serialize(deserialize( setup )) == setup: expected:', setup, 'got:', res) },
|
||||||
|
|
||||||
'json-caopatiility': function(assert, [setup, json], skipTest){
|
'json-caopatiility': function(assert, [setup, json], skipTest){
|
||||||
if(!json){
|
if(!json){
|
||||||
skipTest()
|
skipTest()
|
||||||
return }
|
return }
|
||||||
var obj = eJSON.deserialize(setup)
|
var obj = eJSON.deserialize(setup, true)
|
||||||
assert(
|
assert(
|
||||||
JSON.stringify(obj) == eJSON.serialize(obj) ) },
|
JSON.stringify(obj) == eJSON.serialize(obj) ) },
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user