I created these functions that will take any object and stringify it, and can then de-stringify it later.
function stringify(objectToCopy, exceptionClasses = []) {
// Listify.
var objects = [];
var que = [objectToCopy];
while(que.length > 0){
var o = que.pop();
objects.push(o);
var propNames = Object.getOwnPropertyNames(o);
for (let i = 0; i < propNames.length; i++) {
const n = propNames[i];
var property = o[n];
if (
typeof property == "object" && property != null &&
objects.indexOf(property) == -1 &&
que.indexOf(property) == -1 &&
exceptionClasses.indexOf(property.constructor.name) == -1
) {
que.push(property);
}
}
}
// Stringify.
var stringifiedList = [];
for (let i = 0; i < objects.length; i++) {
const o = objects[i];
var temp = {
"className": o.constructor.name,
"properties": [],
};
var propNames = Object.getOwnPropertyNames(o);
for (let i = 0; i < propNames.length; i++) {
const n = propNames[i];
var property = o[n];
if (typeof property == "object" && property != null){
if (exceptionClasses.indexOf(property.constructor.name) == -1) {
temp.properties.push({
"isObject": true,
"name": n,
"content": objects.indexOf(property), // content will here refer to the index of where the object is.
});
}
}else{
var content = JSON.stringify(property);
if (content == null) {
content = null; // Converts from undefined to null.
}
temp.properties.push({
"isObject": false,
"name": n,
"content": content,
});
}
}
stringifiedList.push(temp);
}
//
return JSON.stringify(stringifiedList);
}
function deStringify (string) {
var stringifiedList = JSON.parse(string);
// Listify.
var objects = [];
for (let i = 0; i < stringifiedList.length; i++) {
const data = stringifiedList[i];
var cleanName = /[a-zA-Z_$][0-9a-zA-Z_$]*/.exec(data.className).toString();
if (cleanName != data.className) {
throw "ERROR: Class name validation failed."
}
var newObject = Object.create(eval(cleanName).prototype);
objects.push(newObject);
}
// Destringify
for (let i = 0; i < stringifiedList.length; i++) {
const data = stringifiedList[i];
const obj = objects[i];
for (let j = 0; j < data.properties.length; j++) {
const property = data.properties[j];
if (property.isObject){
obj[property.name] = objects[property.content];
}else{
obj[property.name] = JSON.parse(property.content);
}
}
}
return objects[0];
}
// Testing.
var a = [6,5,4,3];
var b = [a, a, "alpha", "beta"];
b.push(b);
a.push(b);
var s = stringify(a);
console.log(deStringify(s));
/* Output:
(5) [6, 5, 4, 3, Array(5)]
0: 6
1: 5
2: 4
3: 3
4: Array(5)
0: (5) [6, 5, 4, 3, Array(5)]
1: (5) [6, 5, 4, 3, Array(5)]
2: "alpha"
3: "beta"
4: (5) [Array(5), Array(5), "alpha", "beta", Array(5)]
length: 5
__proto__: Array(0)
length: 5
__proto__: Array(0)
*/
The reason I don't only use JSON.stringify()
is because it does not conserve the prototype, and also does not support loop references. Is this approach hacky? Is there something about the objects I duplicate that I am missing when I only duplicate its properties? I know this code doesn't work for DOM objects or objects related to webgl.