最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Serializing and deserializing functions to and from JSON - Stack Overflow

programmeradmin2浏览0评论

Suppose you have the following:

function myfunc() {
  // JS code
}

var args = '{ "strfield": "hello world", "numfield": 10, "funcfield": myfunc }';

The problem: how do you process the args variable before submitting it to the JSON parser so that myfunc is replaced with the result of myfunc.toString() (that is, the body of the function)? The proposed solution should work on arbitrary functions and such quasi-JSON strings.

Suppose you have the following:

function myfunc() {
  // JS code
}

var args = '{ "strfield": "hello world", "numfield": 10, "funcfield": myfunc }';

The problem: how do you process the args variable before submitting it to the JSON parser so that myfunc is replaced with the result of myfunc.toString() (that is, the body of the function)? The proposed solution should work on arbitrary functions and such quasi-JSON strings.

Share Improve this question edited Aug 31, 2014 at 1:47 AstroCB 12.4k20 gold badges59 silver badges74 bronze badges asked Nov 27, 2010 at 20:33 dpqdpq 9,28812 gold badges53 silver badges71 bronze badges 7
  • Interesting... but I cannot imagine that there is a way to actually get the code in the body of the function as string. But I can be wrong and if there is way, that would be awesome :) – Felix Kling Commented Nov 27, 2010 at 20:36
  • Huh? There is such a way: that myfunc.toString() call didn't e out of nowhere :P developer.mozilla/en/JavaScript/Reference/Global_Objects/… The problem is not getting the function's code, but rather elegantly substituting the fields' value with it without having to mutilate the JSON parser. – dpq Commented Nov 27, 2010 at 20:45
  • I suppose (assuming toString is cross-platform) that you could parse the args string into an object, iterate through every property in the object (recursively), and if the value of any property is defined and is a function, replace it with that function's code, then convert that object back to JSON. The question then bees, "How do I detect if a JS object is a function?" – Cameron Commented Nov 27, 2010 at 20:52
  • Using typeof field == "function". What you propose would generate a double overhead (and even more, if you pare against native JSON parsers), although it will certainly work... Can you advice any improvements? – dpq Commented Nov 27, 2010 at 21:02
  • @David: I realize the overhead is large (and no immediate improvements e to mind), but I can't think of any other way. I wouldn't worry about performance until it bees an issue. And I didn't know about typeof field == "function" -- cool! – Cameron Commented Nov 27, 2010 at 21:14
 |  Show 2 more ments

2 Answers 2

Reset to default 7

We use the optional second replacer argument to JSON.stringify to preprocess key/values, emitting functions in stringified form:

function stringify_with_fns(obj) {
    return JSON.stringify(obj, function(key, value) {
        return typeof value === "function" ? value.toString() : value;
    });
}

Then to turn the stringified functions back into real functions on the way out, we use the optional second reviver parameter to JSON.parse, as in

function parse_with_fns(json) {
    return JSON.parse(json, function(key, value) {
        if (looks_like_a_function_string(value)) {
            return make_function_from_string(value);
        } else {
            return value; 
        }
    });
}

looks_like_a_function_string is just a regexp, and the first cut at make_function_from_string can use eval:

function looks_like_a_function_string(value) {
    return /^function.*?\(.*?\)\s*\{.*\}$/.test(value);
}
function make_function_from_string(value) {
    return eval(value);
}

To avoid eval, we can pick apart the function string to find its arguments and body, so we can pass them to new Function:

function make_function_from_string(value) {
    var args = value
        .replace(/\/\/.*$|\/\*[\s\S]*?\*\//mg, '') //strip ments
        .match(/\(.*?\)/m)[0]                      //find argument list
        .replace(/^\(|\)$/, '')                    //remove parens
        .match(/[^\s(),]+/g) || [],                //find arguments
        body = value.match(/\{(.*)\}/)[1]          //extract body between curlies

    return Function.apply(0, args.concat(body);
}

Testing:

x = parse_with_fns(stringify_with_fns({a: function() {var x=1;}}))
x.a
> function anonymous() {var x=1;}

Note however that since functions created by new Function are all in the global scope, they will lose their enclosing scope and closures.

The only remaining question is whether this is useful. I suppose it might be for small utility functions.

Extending the concept to serializing/deserializing regexps or date objects is left as an exercise.

Like this? I had to change it because what you have isn't a valid JSON string since myfunc doesn't have double quotes. So I added those.

This parses it, gets the value of funcfield, finds the function (although it assumes it is in the global context), calls toString() updating the value of funcfield, the re-stringifies it as JSON.

Example: http://jsfiddle/patrick_dw/QUR6Z/

var myfunc = function() {
    alert('hi');
};

var args = '{ "strfield": "hello world", "numfield": 10, "funcfield": "myfunc" }';
var parsed = JSON.parse( args );

parsed.funcfield = window[parsed.funcfield].toString();
var stringified = JSON.stringify( parsed );

alert(stringified);

Was this what you meant?


EDIT:

I guess you could use the current context, as long as that context is contained within the context that owns the function.

parsed.funcfield = this[parsed.funcfield].toString();

Or if you can't double quote the function name, you could use eval instead if you're certain the data is safe.

Example: http://jsfiddle/patrick_dw/QUR6Z/1/

var args = '{ "strfield": "hello world", "numfield": 10, "funcfield": myfunc }';
window.eval( 'var parsed = ' + args );

parsed.funcfield = parsed.funcfield.toString();
var stringified = JSON.stringify( parsed );

alert(stringified);
发布评论

评论列表(0)

  1. 暂无评论