2011-10-13 6 views
13

Tome este objeto:¿Hay alguna posibilidad de tener las funciones de preservación de JSON.stringify?

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

Si hago esto:

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

entonces Y volverá { "key1": "xxx" }. ¿Hay algo que uno pueda hacer para transferir funciones a través de stringify? La creación de un objeto con funciones adjuntas es posible con el "ye goode olde eval()", pero ¿qué pasa con el empaquetado?

+0

por qué no añadir comillas alrededor de su función y 'eval()' se ¿luego? – Blender

+1

JSON no permite funciones. Si lo hiciera, no sería mejor ni más seguro que 'eval'. – cHao

+0

¿Es esto para lo que 'toJSON' es? – bzlm

Respuesta

4

No puede empaquetar las funciones ya que los datos que cierran no son visibles para ningún serializador. Incluso el uneval de Mozilla no puede embalar los cierres correctamente.

Su mejor opción es usar un reviver y un sustituto.

http://developer.yahoo.com/yui/examples/json/json_freeze_thaw.html

La función resucitador pasado a JSON.parse se aplica a todas clave: pares de valores en el objeto parsed prima a partir de las teclas más profundos al nivel más alto. En nuestro caso, esto significa que el nombre y las propiedades descubiertas se pasarán a través del rescatador, y luego se pasará el objeto que contiene esas claves.

3

Técnicamente esto no es JSON, también puedo apenas imaginar por qué le gustaría hacer esto, pero trata este truco:

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

Por supuesto, la primera línea se puede automatizar mediante la iteración recursiva sobre el objeto . La operación inversa es más difícil - la función es solo una cadena, eval funcionará, pero tiene que suponer si una clave dada contiene un código de función stringified o no.

1

Que yo sepa, no hay bibliotecas de serialización que persistan funciones - en cualquier idioma. La serialización es lo que se hace para preservar los datos. La compilación es lo que se hace para preservar las funciones.

+4

Hay muchos idiomas donde las funciones * son * datos. – Bergi

6

Me encontré con el mismo problema, Hubo otra publicación similar a la suya que se encontró json-stringify-function. lo siguiente puede ser útil para usted:

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; 
    }); 
    } 
}()); 

Fragmento de código tomado de Vadim Kiryukhin de JSONfn.js o ver la documentación en Home Page

1

Sí, se puede hacer esto con JSONfn plugin.

Tome un vistazo a http://www.eslinstructor.net/jsonfn/

Es exactamente lo que estás buscando.

+1

Ese es el mismo complemento/código que publiqué en julio;) Me alegra ver que no soy el único que piensa que es la solución jaja –

1

Parece que las personas que desembarcan aquí están tratando con estructuras que serían JSON válidas si no fuera por el hecho de que contienen funciones. Entonces, ¿cómo manejamos la stringificación de estas estructuras?

Me encontré con el problema mientras escribía un script para modificar las configuraciones de RequireJS. Así es como lo hice. En primer lugar, hay un poco de código anterior que asegura que el marcador de posición utilizado internamente (">>>F<<<") no aparece como un valor en la configuración RequireJS.Muy poco probable que suceda, pero es mejor prevenir que lamentar. La configuración de entrada se lee como un objeto JavaScript, que puede contener matrices, valores atómicos, otros Object sy funciones. Sería directamente codificable como JSON si las funciones no estuvieran presentes. Esta configuración es el objeto config en el código que sigue:

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

La variable post contiene el valor final. Este código se basa en el hecho de que el orden en el que se llama al handler es el mismo que el orden de los diversos datos en el JSON final. Revisé la 5ª edición de ECMAScript, que define el algoritmo de stringificación y no se puede encontrar un caso en el que haya un problema de pedido. Si este algoritmo fuera a cambiar en una edición futura, la solución sería usar placholders únicos para la función y usarlos para referirse a las funciones que se almacenarían en una matriz asociativa asignando marcadores de posición únicos a las funciones.

1

El traviesa pero eficaz manera sería simplemente:

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

Aunque el verdadero problema (aparte de la modificación del prototipo de Function) sería deserialización sin el uso deeval.

0

Es enteramente posible crear funciones de cadena sin 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 }"}" 
*/ 

ahora un poco de magia para convertir cadena en función con esta funció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); 
} 

ahora utilizar JSON.parse con resucitador

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 
0

Esto es lo que hice 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; 
    }); 
}; 
Cuestiones relacionadas