Aquí hay un ejemplo de una estructura de datos con referencias cíclicas:
function makeToolshed(){
var nut = {name: 'nut'}, bolt = {name: 'bolt'};
nut.needs = bolt; bolt.needs = nut;
return { nut: nut, bolt: bolt };
}
Cuando desee MANTENER las referencias cíclicas (restaurarlas cuando deserialice, en lugar de "destruirlas"), tiene 2 opciones, que compararé aquí. Primero es el ciclo.js de Douglas Crockford , segundo es mi paquete de siberia . Ambos trabajan primero "desciclando" el objeto, es decir, construyendo otro objeto (sin ninguna referencia cíclica) "que contenga la misma información".
Crockford va primero:
JSON.decycle(makeToolshed())
Como puede ver, la estructura anidada de JSON se conserva, pero hay una cosa nueva, que son los objetos con la $ref
propiedad especial . Veamos cómo funciona eso.
root = makeToolshed();
[root.bolt === root.nut.needs, root.nut.needs.needs === root.nut]; // retutrns [true,true]
El signo de dólar representa la raíz. .bolt
Tener $ref
nos dice que .bolt
es un objeto "ya visto", y el valor de esa propiedad especial (aquí, la cadena $ ["tuerca"] ["necesita"]) nos dice dónde, ver primero ===
arriba. Asimismo para el segundo $ref
y el segundo ===
arriba.
Usemos una prueba de igualdad profunda adecuada (es decir, la deepGraphEqual
función de Anders Kaseorg de la respuesta aceptada a esta pregunta ) para ver si la clonación funciona.
root = makeToolshed();
clone = JSON.retrocycle(JSON.decycle(root));
deepGraphEqual(root, clone) // true
serialized = JSON.stringify(JSON.decycle(root));
clone2 = JSON.retrocycle(JSON.parse(serialized));
deepGraphEqual(root, clone2); // true
Ahora, siberia:
JSON.Siberia.forestify(makeToolshed())
Siberia no intenta imitar JSON "clásico", no hay estructura anidada. El gráfico del objeto se describe de manera "plana". Cada nodo del gráfico de objeto se convierte en un árbol plano (lista de pares de valores de clave simple con valores enteros), que es una entrada en .forest.
En el índice cero, encontramos el objeto raíz, en índices más altos, encontramos los otros nodos de el gráfico de objetos y los valores negativos (de alguna clave de algún árbol del bosque) apuntan a la atoms
matriz (que se escribe a través de la matriz de tipos, pero omitiremos los detalles de escritura aquí). Todos los nodos terminales están en la tabla de átomos, todos los nodos no terminales están en la tabla del bosque, y puede ver de inmediato cuántos nodos tiene el gráfico de objeto, a saber forest.length
. Probemos si funciona:
root = makeToolshed();
clone = JSON.Siberia.unforestify(JSON.Siberia.forestify(root));
deepGraphEqual(root, clone); // true
serialized = JSON.Siberia.stringify(JSON.Siberia.forestify(root));
clone2 = JSON.Siberia.unforestify(JSON.Siberia.unstringify(serialized));
deepGraphEqual(root, clone2); // true
comparación
agregará la sección más tarde.