Skip to content

Instantly share code, notes, and snippets.

@jozefchutka
Created May 6, 2022 11:58
Show Gist options
  • Select an option

  • Save jozefchutka/dd3bcb37589a45f21bbad577c84fa862 to your computer and use it in GitHub Desktop.

Select an option

Save jozefchutka/dd3bcb37589a45f21bbad577c84fa862 to your computer and use it in GitHub Desktop.
Self-referencing JSON to handle recursion, WDYT?
function stringify(value) {
const map = new Map();
function traverse(value, reference) {
if(map.has(value))
return map.get(value);
if(Array.isArray(value)) {
map.set(value, reference);
let result = "";
const last = value.length - 1;
for(let i = 0; i <= last; i++) {
result += traverse(value[i], reference + "[" + i + "]");
if(i !== last)
result += ",";
}
return "[" + result + "]";
}
if (value !== null && typeof value === "object") {
map.set(value, reference);
const keys = Object.keys(value);
const lastKey = keys[keys.length-1];
let result = "";
for (const key of keys) {
result += JSON.stringify(key) + ":" + traverse(value[key], reference + "." + key);
if (key !== lastKey)
result += ",";
}
return "{" + result + "}";
}
return JSON.stringify(value);
}
return traverse(value, "_");
}
function parse(text) {
const normalizeKey = key => /^[0-9]+$/.test(key) ? parseInt(key) : key;
const proxies = new Set();
function proxy(reference) {
const result = new Proxy(() => reference, {
apply:(target, thisArg, [root]) => target().reduce((p, c) => p[normalizeKey(c)], root),
get:(target, property) => proxy([...target(), property])
});
proxies.add(result);
return result;
}
function traverse(data) {
if(Array.isArray(data)) {
for(let i = 0; i < data.length; i++) {
const value = data[i];
if(proxies.has(value))
data[i] = value(_);
else
traverse(value);
}
} else if (data !== null && typeof data === "object") {
for (const key of Object.keys(data)) {
const value = data[key];
if(proxies.has(value))
data[key] = value(_);
else
traverse(value);
}
}
return data;
}
const _ = (new Function("with(this) return " + text)).call({_:proxy([])});
return traverse(_);
}
let foo = {foo:"bar"};
let source = [foo, foo];
source.push(source);
// `_` refers to the object itself
console.log(stringify(source)); // [{"foo":"bar"},_[0],_]
let result = parse('[{"foo":"bar"},_[0],_]');
console.log(result[0] === result[1]); // true
console.log(result[1].foo === "bar"); // true
console.log(result[2] === result); // true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment