Сериализация и десериализация функций в и из JSON - PullRequest
4 голосов
/ 27 ноября 2010

Предположим, у вас есть следующее:

function myfunc() {
  // JS code
}

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

Проблема: как вы обрабатываете переменную args перед отправкой ее в анализатор JSON, так что myfunc заменяется результатом myfunc.toString() (то есть тело функции)?Предлагаемое решение должно работать с произвольными функциями и такими квази-JSON-строками.

Ответы [ 2 ]

5 голосов
/ 30 августа 2014

Мы используем необязательный второй аргумент replacer для JSON.stringify для предварительной обработки ключа / значений, выпуская функции в строковом виде:

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

Затем, чтобы превратить строковые функции в действительныефункции на выходе, мы используем дополнительный второй reviver параметр для JSON.parse, как в

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 это просто регулярное выражение, а первый вырез на make_function_from_string может использовать eval:

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

Чтобы избежать eval, мы можем выделить строку функции, чтобы найти ее аргументы и тело, чтобы мы могли передать их new Function:

function make_function_from_string(value) {
    var args = value
        .replace(/\/\/.*$|\/\*[\s\S]*?\*\//mg, '') //strip comments
        .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);
}

Тестирование:

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

Обратите внимание, однако, что поскольку все функции, созданные с помощью new Function, находятся в глобальной области видимости, они теряют свою объемную область и замыкания.

Остается вопрос: полезно ли это?Я предполагаю, что это может быть для небольших служебных функций.

Расширение концепции для сериализации / десериализации регулярных выражений или объектов даты оставлено в качестве упражнения.

0 голосов
/ 28 ноября 2010

Как это?Мне пришлось изменить это, потому что то, что у вас есть, не является допустимой строкой JSON, так как myfunc не имеет двойных кавычек.Поэтому я добавил их.

Это анализирует его, получает значение funcfield, находит функцию (хотя и предполагает, что она находится в глобальном контексте), вызывает toString(), обновляя значение funcfield,он переформатирует его в JSON.

Пример: http://jsfiddle.net/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);

Это то, что вы имели в виду?


РЕДАКТИРОВАТЬ:

Я думаю, вы могли бы использовать текущий контекст, если этот контекст содержится в контексте, который владеет функцией.

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

Или, если вы можетене вводите двойную кавычку имени функции, вместо этого вы можете использовать eval, если уверены, что данные в безопасности.

Пример: http://jsfiddle.net/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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...