var XJSON = new function () {
function isPureObject(obj) { return !!obj && typeof obj == 'object' && !(obj instanceof Array)}
this.parse = function (xjson) {
var refs = JSON.parse(xjson)
var vals = refs.map(function (ref) {
return isPureObject(ref) ? {} : ref
})
refs.reduceRight(function (_ ,ref, id) {
if (!isPureObject(ref)) return
Object.keys(ref).forEach(function (key) {
vals[id][key] = vals[ref[key]]
})
}, undefined)
return vals[0]
}
this.stringify = function (obj) {
var refs = [], vals = []
function conv(obj, id) {
vals[id] = refs[id] = obj
if (!isPureObject(obj)) return
var ref = refs[id] = {}
Object.getOwnPropertyNames(obj).forEach(function (key) {
var val = obj[key]
var id = ref[key] = vals.indexOf(val)
if (id != -1) return
var newid = ref[key] = vals.length
conv(val, newid)
})
}
conv(obj, 0)
return JSON.stringify(refs)
}
}
function test(id, obj) {
console.group('test ' + id)
var xjson = XJSON.stringify(obj)
console.log(xjson)
var xobj = XJSON.parse(xjson)
console.dir(xobj)
console.dir(obj)
console.groupEnd('test ' + id)
}
function testing() {
var obj1 = { o: {} }
obj1.o = obj1
var obj2 = {
a: {
b: {
c: 'xyz',
d: {},
e: 123,
f: {},
g: [true, false]
},
h: {}
}
}
obj2.a.b.d = obj2
obj2.a.b.f = obj2.a
obj2.a.h = obj2.a.b
test(1, obj1)
test(2, obj2)
}
testing()
dmFyIFhKU09OID0gbmV3IGZ1bmN0aW9uICgpIHsKCglmdW5jdGlvbiBpc1B1cmVPYmplY3Qob2JqKSB7IHJldHVybiAhIW9iaiAmJiB0eXBlb2Ygb2JqID09ICdvYmplY3QnICYmICEob2JqIGluc3RhbmNlb2YgQXJyYXkpfQoKCgl0aGlzLnBhcnNlID0gZnVuY3Rpb24gKHhqc29uKSB7CgoJCXZhciByZWZzID0gSlNPTi5wYXJzZSh4anNvbikKCgkJdmFyIHZhbHMgPSByZWZzLm1hcChmdW5jdGlvbiAocmVmKSB7CgkJCXJldHVybiBpc1B1cmVPYmplY3QocmVmKSA/IHt9IDogcmVmCgkJfSkKCgkJcmVmcy5yZWR1Y2VSaWdodChmdW5jdGlvbiAoXyAscmVmLCBpZCkgewoJCQlpZiAoIWlzUHVyZU9iamVjdChyZWYpKSByZXR1cm4KCQkJT2JqZWN0LmtleXMocmVmKS5mb3JFYWNoKGZ1bmN0aW9uIChrZXkpIHsKCQkJCXZhbHNbaWRdW2tleV0gPSB2YWxzW3JlZltrZXldXQoJCQl9KQoJCX0sIHVuZGVmaW5lZCkKCgkJcmV0dXJuIHZhbHNbMF0KCgl9CgoKCXRoaXMuc3RyaW5naWZ5ID0gZnVuY3Rpb24gKG9iaikgewoKCQl2YXIgcmVmcyA9IFtdLCB2YWxzID0gW10KCgoJCWZ1bmN0aW9uIGNvbnYob2JqLCBpZCkgewoKCQkJdmFsc1tpZF0gPSByZWZzW2lkXSA9IG9iagoJCQlpZiAoIWlzUHVyZU9iamVjdChvYmopKSByZXR1cm4KCQkJdmFyIHJlZiA9IHJlZnNbaWRdID0ge30KCgkJCU9iamVjdC5nZXRPd25Qcm9wZXJ0eU5hbWVzKG9iaikuZm9yRWFjaChmdW5jdGlvbiAoa2V5KSB7CgoJCQkJdmFyIHZhbCA9IG9ialtrZXldCgkJCQl2YXIgaWQgPSByZWZba2V5XSA9IHZhbHMuaW5kZXhPZih2YWwpCgkJCQlpZiAoaWQgIT0gLTEpIHJldHVybgoJCQkJdmFyIG5ld2lkID0gcmVmW2tleV0gPSB2YWxzLmxlbmd0aAoKCQkJCWNvbnYodmFsLCBuZXdpZCkKCgkJCX0pCgkJfQoKCgkJY29udihvYmosIDApCgkJcmV0dXJuIEpTT04uc3RyaW5naWZ5KHJlZnMpCgoJfQoKfQoKCgpmdW5jdGlvbiB0ZXN0KGlkLCBvYmopIHsKCgljb25zb2xlLmdyb3VwKCd0ZXN0ICcgKyBpZCkKCXZhciB4anNvbiA9IFhKU09OLnN0cmluZ2lmeShvYmopCgljb25zb2xlLmxvZyh4anNvbikKCXZhciB4b2JqICA9IFhKU09OLnBhcnNlKHhqc29uKQoJY29uc29sZS5kaXIoeG9iaikKCWNvbnNvbGUuZGlyKG9iaikKCWNvbnNvbGUuZ3JvdXBFbmQoJ3Rlc3QgJyArIGlkKQoKfQoKCgpmdW5jdGlvbiB0ZXN0aW5nKCkgewoKCXZhciBvYmoxID0geyBvOiB7fSB9CglvYmoxLm8gPSBvYmoxCgoJdmFyIG9iajIgPSB7CgkJYTogewoJCQliOiB7CgkJCQljOiAneHl6JywKCQkJCWQ6IHt9LAoJCQkJZTogMTIzLAoJCQkJZjoge30sCgkJCQlnOiBbdHJ1ZSwgZmFsc2VdCgkJCX0sCgkJCWg6IHt9CgkJfSAKCX0KCglvYmoyLmEuYi5kID0gb2JqMgoJb2JqMi5hLmIuZiA9IG9iajIuYQoJb2JqMi5hLmggPSBvYmoyLmEuYgoKCXRlc3QoMSwgb2JqMSkKCXRlc3QoMiwgb2JqMikKCn0KCgp0ZXN0aW5nKCk=