是否有可能有JSON.对保存函数进行字符串化
Is there any possibility to have JSON.stringify preserve functions?
取这个对象:
x = {
"key1": "xxx",
"key2": function(){return this.key1}
}
如果我这样做:
y = JSON.parse( JSON.stringify(x) );
则y将返回{ "key1": "xxx" }
。有什么方法可以通过stringify传递函数吗?创建带有附加函数的对象可以使用"旧的eval()",但是打包它呢?
json-stringify-function是一个类似的帖子。
通过该帖子发现的片段可能对任何偶然发现此答案的人有用。它通过使用JSON中的replacer参数来工作。JSON.parse.
中的stringify和reviver参数更具体地说,当一个值恰好是函数类型时,通过replacer
对其调用.toString()
。当需要解析时,当函数以字符串形式出现时,通过reviver
执行eval()
。
var JSONfn;
if (!JSONfn) {
JSONfn = {};
}
(function () {
JSONfn.stringify = function(obj) {
return JSON.stringify(obj,function(key, value){
return (typeof value === 'function' ) ? value.toString() : value;
});
}
JSONfn.parse = function(str) {
return JSON.parse(str,function(key, value){
if(typeof value != 'string') return value;
return ( value.substring(0,8) == 'function') ? eval('('+value+')') : value;
});
}
}());
代码片段取自Vadim Kiryukhin的JSONfn.js或参见主页的文档
我最近也有类似的需求。为了清楚,输出看起来像JSON,但实际上只是javascript。
JSON.stringify
在大多数情况下工作良好,但在函数中"失败"。
我用一些小技巧使它工作:
- 利用
replacer
(JSON.stringify()
的第二个参数) - 使用
func.toString()
获取函数的JS代码 - 记住哪些函数已被字符串化,并在结果 中直接替换它们
它是这样的:
// our source data
const source = {
"aaa": 123,
"bbb": function (c) {
// do something
return c + 1;
}
};
// keep a list of serialized functions
const functions = [];
// json replacer - returns a placeholder for functions
const jsonReplacer = function (key, val) {
if (typeof val === 'function') {
functions.push(val.toString());
return "{func_" + (functions.length - 1) + "}";
}
return val;
};
// regex replacer - replaces placeholders with functions
const funcReplacer = function (match, id) {
return functions[id];
};
const result = JSON
.stringify(source, jsonReplacer) // generate json with placeholders
.replace(/"'{func_('d+)'}"/g, funcReplacer); // replace placeholders with functions
// show the result
document.body.innerText = result;
body { white-space: pre-wrap; font-family: monospace; }
重要提示:注意占位符的格式-确保它不是太通用。如果要更改它,也要根据需要更改正则表达式。
从技术上讲,这不是JSON,我也很难想象为什么你想这样做,但尝试以下hack:
x.key2 = x.key2.toString();
JSON.stringify(x) //"{"key1":"xxx","key2":"function (){return this.key1}"}"
当然,第一行可以通过递归迭代对象来自动完成。反向操作更难——函数只是一个字符串,eval
可以工作,但是你必须猜测给定的键是否包含字符串化的函数代码。
不能打包函数,因为它们封闭的数据对任何序列化器都是不可见的。即使Mozilla的uneval
也不能正确打包闭包。
你最好的选择是使用一个恢复器和一个替换器。
https://yuilibrary.com/yui/docs/json/json-freeze-thaw.html传递给JSON的reviver函数。Parse应用于原始解析对象中的所有键:值对,从最深层的键到最高级。在我们的示例中,这意味着名称和发现的属性将通过检索器传递,然后包含这些键的对象将通过检索器传递。
我就是这么做的https://gist.github.com/Lepozepo/3275d686bc56e4fb5d11d27ef330a8ed
function stringifyWithFunctions(object) {
return JSON.stringify(object, (key, val) => {
if (typeof val === 'function') {
return `(${val})`; // make it a string, surround it by parenthesis to ensure we can revive it as an anonymous function
}
return val;
});
};
function parseWithFunctions(obj) {
return JSON.parse(obj, (k, v) => {
if (typeof v === 'string' && v.indexOf('function') >= 0) {
return eval(v);
}
return v;
});
};
我已经提出了这个解决方案,它将照顾函数的转换(没有eval
)。您所要做的就是将这些代码放在使用JSON
方法之前。用法完全相同,但现在只需要一个参数value
来转换为JSON字符串,所以如果你传递剩余的replacer
和space
参数,它们将被忽略。
void function () {
window.JSON = Object.create(JSON)
JSON.stringify = function (obj) {
return JSON.__proto__.stringify(obj, function (key, value) {
if (typeof value === 'function') {
return value.toString()
}
return value
})
}
JSON.parse = function (obj) {
return JSON.__proto__.parse(obj, function (key, value) {
if (typeof value === 'string' && value.slice(0, 8) == 'function') {
return Function('return ' + value)()
}
return value
})
}
}()
// YOUR CODE GOES BELOW HERE
x = {
"key1": "xxx",
"key2": function(){return this.key1}
}
const y = JSON.parse(JSON.stringify(x))
console.log(y.key2())
顽皮但有效的方法是:
Function.prototype.toJSON = function() { return this.toString(); }
虽然你真正的问题(除了修改Function
的原型)将是反序列化没有使用eval
.
完全有可能从字符串创建函数没有eval()
var obj = {a:function(a,b){
return a+b;
}};
var serialized = JSON.stringify(obj, function(k,v){
//special treatment for function types
if(typeof v === "function")
return v.toString();//we save the function as string
return v;
});
/*output:
"{"a":"function (a,b){'n return a+b;'n }"}"
*/
现在有一些神奇的将字符串转换为函数的功能
var compileFunction = function(str){
//find parameters
var pstart = str.indexOf('('), pend = str.indexOf(')');
var params = str.substring(pstart+1, pend);
params = params.trim();
//find function body
var bstart = str.indexOf('{'), bend = str.lastIndexOf('}');
var str = str.substring(bstart+1, bend);
return Function(params, str);
}
现在使用JSON。解析
var revivedObj = JSON.parse(serialized, function(k,v){
// there is probably a better way to determ if a value is a function string
if(typeof v === "string" && v.indexOf("function") !== -1)
return compileFunction(v);
return v;
});
//output:
revivedObj.a
function anonymous(a,b
/**/) {
return a+b;
}
revivedObj.a(1,2)
3
据我所知,在任何语言中都没有持久化函数的序列化库。序列化是为了保存数据而做的事情。编译是用来保存函数的。
似乎在这里着陆的人正在处理结构,如果不是因为它们包含函数,它们将是有效的JSON。那么我们如何处理这些结构的字符串化呢?
我在编写修改RequireJS配置的脚本时遇到了这个问题。我是这样做的。首先,前面有一些代码确保内部使用的占位符(">>>F<<<"
)不会在RequireJS配置中显示为值。不太可能发生,但安全总比后悔好。输入配置作为JavaScript Object读取,该对象可能包含数组、原子值、其他Object
和函数。如果函数不存在,它可以直接字符串化为JSON。这个配置是下面代码中的config
对象:
// Holds functions we encounter.
var functions = [];
var placeholder = ">>>F<<<";
// This handler just records a function object in `functions` and returns the
// placeholder as the value to insert into the JSON structure.
function handler(key, value) {
if (value instanceof Function) {
functions.push(value);
return placeholder;
}
return value;
}
// We stringify, using our custom handler.
var pre = JSON.stringify(config, handler, 4);
// Then we replace the placeholders in order they were encountered, with
// the functions we've recorded.
var post = pre.replace(new RegExp('"' + placeholder + '"', 'g'),
functions.shift.bind(functions));
post
变量包含最终值。这段代码依赖于这样一个事实,即调用handler
的顺序与最终JSON中各个数据块的顺序相同。我检查了ECMAScript第5版,它定义了字符串化算法,没有发现排序问题的情况。如果该算法在将来的版本中发生更改,则可以为函数使用唯一占位符,并使用这些占位符引用将存储在将唯一占位符映射到函数的关联数组中的函数。
- 如何在JavaScript中将字符串转换为函数引用
- jQuery中是否内置了任何字符串格式化函数
- JavaScript按钮/输入/函数,字符串反转
- 推荐在JavaScript中执行存储为字符串的函数,而不是使用eval
- 编辑输出字符串函数ol.control.MouseControl
- 如何在 Javascript 和 qUnit 中模拟字符串函数
- 如何在for()语句中执行字符串函数
- GulpJS:对SVG文件内容执行字符串函数
- 在干净的字符串函数之间添加空格
- 错误将导致最长重复子字符串函数
- Javascript:替换在IE7-8中不工作的字符串函数
- 如何使用拆分字符串函数的结果在Angular.js中进行排序
- 反转不工作的字符串函数
- 在Python中,你可以像在JavaScript中那样创建字符串函数的原型
- 我如何使用简单的子字符串函数在javascript标题大小写句子
- Javascript字符串函数
- 来自字符串函数的图像
- 有没有任何方法可以在不使用javascript子字符串函数或regex的情况下生成这个字符串日期表示(带时区)
- 在从CFC返回结果之前,对AJAX查询执行字符串函数
- 不能调用下划线.字符串函数' lpad '用于错误'未捕获的类型错误:_.Lpad不是一个函数