有没有可能让JSON.stringify保留函数?

时间:2011-10-13 19:09:18

标签: javascript json object

拿这个对象:

x = {
 "key1": "xxx",
 "key2": function(){return this.key1}
}

如果我这样做:

y = JSON.parse( JSON.stringify(x) );

然后y将返回{ "key1": "xxx" }。有没有什么可以通过stringify传递函数?使用“ye goode olde eval()”创建具有附加功能的对象,但包装它的是什么?

11 个答案:

答案 0 :(得分:8)

我遇到了同样的问题,还有另一篇类似于你的帖子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;
    });
  }
}());

从Vadim Kiryukhin JSONfn.js获取的代码段或参见Home Page

的文档

答案 1 :(得分:5)

从技术上讲这不是JSON,我也很难想象你为什么要这样做,但尝试以下黑客攻击:

x.key2 = x.key2.toString();
JSON.stringify(x)  //"{"key1":"xxx","key2":"function (){return this.key1}"}"

当然,第一行可以通过在对象上递归迭代来自动化。反向操作更难 - 函数只是一个字符串,eval将起作用,但您必须猜测给定的键是否包含字符串化的函数代码。

答案 2 :(得分:4)

您无法打包功能,因为它们关闭的数据对任何序列化程序都不可见。 即使是Mozilla的uneval也无法正确打包。

你最好的选择是使用复活者和替代者。

https://yuilibrary.com/yui/docs/json/json-freeze-thaw.html

  

传递给JSON.parse的reviver函数应用于原始解析对象中从最深键到最高级别的所有键:值对。在我们的例子中,这意味着名称和已发现的属性将通过reviver传递,然后包含这些键的对象将被传递。

答案 3 :(得分:3)

我最近有类似的要求。要清楚,输出看起来像,但实际上只是javascript。

JSON.stringify在大多数情况下运作良好,但"失败"有功能。

我使用了一些技巧:

  1. 使用replacer (2nd parameter of JSON.stringify())
  2. 使用func.toString()获取函数的JS代码
  3. 记住哪些函数已被字符串化并直接在结果
  4. 中替换它们

    以下是它的样子:

    
    
    // 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; }
    
    
    

    重要提示:请注意占位符格式 - 确保它不太通用。如果您更改它,也可以根据需要更改正则表达式。

答案 4 :(得分:2)

顽皮但有效的方式就是:

Function.prototype.toJSON = function() { return this.toString(); }

虽然你真正的问题(除了修改Function的原型之外)会反序列化而不使用使用eval

答案 5 :(得分:2)

这就是我所做的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;
  });
};

答案 6 :(得分:1)

据我所知,没有任何序列化库可以保留函数 - 使用任何语言。序列化是保护数据的方法。编译是保存函数的方法。

答案 7 :(得分:1)

是的,您可以使用JSONfn插件执行此操作。

查看 http://www.eslinstructor.net/jsonfn/

这正是你要找的。

答案 8 :(得分:1)

似乎登陆这里的人正在处理那些有效JSON的结构,如果不是因为它们包含函数。那么我们如何处理这些结构的字符串呢?

我在编写脚本以修改RequireJS配置时遇到了问题。这就是我做到的。首先,前面有一些代码可以确保内部使用的占位符(">>>F<<<")不会显示为RequireJS配置中的值。不太可能发生,但比抱歉更安全。输入配置被读作JavaScript对象,它可能包含数组,原子值,其他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版,它定义了字符串化算法,并且找不到会出现排序问题的情况。如果要在将来的版本中更改此算法,则修复将使用独特的placholders用于函数,并使用这些函数来引用将存储在将唯一占位符映射到函数的关联数组中的函数。

答案 9 :(得分:1)

完全可以从字符串创建函数而不使用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.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

答案 10 :(得分:0)

我写了几乎同样的问题 https://github.com/simone-sanfratello/jsfy

也许可以帮忙