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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 01:13:01  来源:igfitidea点击:

Is there any possibility to have JSON.stringify preserve functions?

javascriptjsonobject

提问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 unevalcannot 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

代码片段取自 Vadim Kiryukhin 的JSONfn.js或查看主页上的文档

回答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, evalwill 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.stringifyworks well in most cases, but "fails" with functions.

JSON.stringify在大多数情况下运行良好,但功能“失败”。

I got it working with a few tricks:

我通过一些技巧让它工作:

  1. make use of replacer(2nd parameter of JSON.stringify())
  2. use func.toString()to get the JS code for a function
  3. remember which functions have been stringified and replace them directly in the result
  1. 使用replacer(的第二个参数JSON.stringify()
  2. 用于func.toString()获取函数的 JS 代码
  3. 记住哪些函数已被字符串化并直接在结果中替换它们

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 Objects and functions. It would be straightforwardly stringifiable as JSON if functions were not present. This configuration is the configobject in the code that follows:

我在编写脚本来修改 RequireJS 配置时遇到了这个问题。我就是这样做的。首先,前面有一些代码确保内部使用的占位符 ( ">>>F<<<") 不会在 RequireJS 配置中显示为值。不太可能发生,但安全总比后悔好。输入配置作为 JavaScript 对象读取,其中可能包含数组、原子值、其他Objects 和函数。如果函数不存在,它将直接字符串化为 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 postvariable contains the final value. This code relies on the fact that the order in which handleris 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,它定义了字符串化算法,但找不到存在排序问题的情况。如果这个算法在未来的版本中改变,修复将是使用唯一的占位符作为函数,并使用这些来引用将存储在将唯一占位符映射到函数的关联数组中的函数。