javascript 有没有可能让 JSON.stringify 保留功能?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7759200/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there any possibility to have JSON.stringify preserve functions?
提问by campino2k
Take this object:
拿这个对象:
x = {
"key1": "xxx",
"key2": function(){return this.key1}
}
If I do this:
如果我这样做:
y = JSON.parse( JSON.stringify(x) );
Then y will return { "key1": "xxx" }
. Is there anything one could do to transfer functions via stringify? Creating an object with attached functions is possible with the "ye goode olde eval()", but whats with packing it?
然后 y 将返回{ "key1": "xxx" }
。有什么办法可以通过 stringify 传递函数吗?使用“ye goode olde eval()”可以创建带有附加函数的对象,但是打包它有什么用?
采纳答案by Mike Samuel
You can't pack functions since the data they close over is not visible to any serializer.
Even Mozilla's uneval
cannot pack closures properly.
您无法打包函数,因为它们关闭的数据对任何序列化程序都是不可见的。即使是 Mozillauneval
也无法正确打包闭包。
Your best bet, is to use a reviver and a replacer.
您最好的选择是使用恢复剂和替代品。
https://yuilibrary.com/yui/docs/json/json-freeze-thaw.html
https://yuilibrary.com/yui/docs/json/json-freeze-thaw.html
The reviver function passed to JSON.parse is applied to all key:value pairs in the raw parsed object from the deepest keys to the highest level. In our case, this means that the name and discovered properties will be passed through the reviver, and then the object containing those keys will be passed through.
传递给 JSON.parse 的 reviver 函数应用于原始解析对象中从最深键到最高级的所有键:值对。在我们的例子中,这意味着名称和发现的属性将通过 reviver,然后包含这些键的对象将通过。
回答by Arthur Weborg
I ran into the same problem, There was another post similar to yours found json-stringify-function. the following may be useful to you:
我遇到了同样的问题,还有另一个类似于你的帖子发现json-stringify-function。以下内容可能对您有用:
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;
});
}
}());
Code Snippet taken from Vadim Kiryukhin's JSONfn.jsor see documentation at Home Page
回答by Tomasz Nurkiewicz
Technically this is not JSON, I can also hardly imagine why would you want to do this, but try the following hack:
从技术上讲,这不是 JSON,我也很难想象您为什么要这样做,但请尝试以下 hack:
x.key2 = x.key2.toString();
JSON.stringify(x) //"{"key1":"xxx","key2":"function (){return this.key1}"}"
Of course the first line can be automated by iterating recursively over the object. Reverse operation is harder - function is only a string, eval
will work, but you have to guesswhether a given key contains a stringified function code or not.
当然,第一行可以通过递归迭代对象来自动化。反向操作更难 - 函数只是一个字符串,eval
可以工作,但您必须猜测给定的键是否包含字符串化的函数代码。
回答by Marz
This is what I did https://gist.github.com/Lepozepo/3275d686bc56e4fb5d11d27ef330a8ed
这就是我所做的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;
});
};
回答by Christian
I've had a similar requirement lately. To be clear, the output lookslike JSON but in fact is just javascript.
我最近也有类似的需求。需要明确的是,输出看起来像 JSON,但实际上只是 javascript。
JSON.stringify
works well in most cases, but "fails" with functions.
JSON.stringify
在大多数情况下运行良好,但功能“失败”。
I got it working with a few tricks:
我通过一些技巧让它工作:
- make use of
replacer
(2nd parameter ofJSON.stringify()
) - use
func.toString()
to get the JS code for a function - remember which functions have been stringified and replace them directly in the result
- 使用
replacer
(的第二个参数JSON.stringify()
) - 用于
func.toString()
获取函数的 JS 代码 - 记住哪些函数已被字符串化并直接在结果中替换它们
And here's how it looks like:
这是它的样子:
// 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; }
Important:Be careful about the placeholder format - make sure it's not too generic. If you change it, also change the regex as applicable.
重要提示:注意占位符格式 - 确保它不太通用。如果您更改它,也请根据需要更改正则表达式。
回答by rounce
The naughty but effectiveway would be to simply:
该顽皮但有效的方法是简单的:
Function.prototype.toJSON = function() { return this.toString(); }
Though your real problem (aside from modifying the prototype of Function
) would be deserialization withoutthe use of eval
.
尽管您真正的问题(除了修改 的原型之外Function
)是在不使用eval
.
回答by Hao Wu
It is entirely possible to create functions from string without 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 }"}"
*/
now some magic to turn string into function with this function
现在有一些魔法可以用这个函数把字符串变成函数
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);
}
now use JSON.parse with reviver
现在使用 JSON.parse 和 reviver
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
回答by Sam Axe
To my knowledge, there are no serialization libraries that persist functions - in any language. Serialization is what one does to preserve data. Compilation is what one does to preserve functions.
据我所知,没有任何语言的序列化库可以持久化函数。序列化是一种保存数据的方式。编译是为了保留函数所做的工作。
回答by Louis
It seems that people landing here are dealing with structures that would be valid JSON if not for the fact that they contain functions. So how do we handle stringifying these structures?
如果不是因为它们包含函数的事实,那么登陆这里的人似乎正在处理将是有效 JSON 的结构。那么我们如何处理这些结构的字符串化呢?
I ran into the problem while writing a script to modify RequireJS configurations. This is how I did it. First, there's a bit of code earlier that makes sure that the placeholder used internally (">>>F<<<"
) does not show up as a value in the RequireJS configuration. Very unlikely to happen but better safe than sorry. The input configuration is read as a JavaScript Object, which may contain arrays, atomic values, other Object
s and functions. It would be straightforwardly stringifiable as JSON if functions were not present. This configuration is the config
object in the code that follows:
我在编写脚本来修改 RequireJS 配置时遇到了这个问题。我就是这样做的。首先,前面有一些代码确保内部使用的占位符 ( ">>>F<<<"
) 不会在 RequireJS 配置中显示为值。不太可能发生,但安全总比后悔好。输入配置作为 JavaScript 对象读取,其中可能包含数组、原子值、其他Object
s 和函数。如果函数不存在,它将直接字符串化为 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));
The post
variable contains the final value. This code relies on the fact that the order in which handler
is called is the same as the order of the various pieces of data in the final JSON. I've checked the ECMAScript 5th edition, which defines the stringification algorithm and cannot find a case where there would be an ordering problem. If this algorithm were to change in a future edition the fix would be to use unique placholders for function and use these to refer back to the functions which would be stored in an associative array mapping unique placeholders to functions.
该post
变量包含的最终值。此代码依赖于handler
调用顺序与最终 JSON 中各种数据的顺序相同的事实。我检查了 ECMAScript 5th edition,它定义了字符串化算法,但找不到存在排序问题的情况。如果这个算法在未来的版本中改变,修复将是使用唯一的占位符作为函数,并使用这些来引用将存储在将唯一占位符映射到函数的关联数组中的函数。