How to Serialize Dom Node to JSON Even If There Are Circular References

Stringify (convert to JSON) a JavaScript object with circular reference

Circular structure error occurs when you have a property of the object which is the object itself directly (a -> a) or indirectly (a -> b -> a).

To avoid the error message, tell JSON.stringify what to do when it encounters a circular reference.
For example, if you have a person pointing to another person ("parent"), which may (or may not) point to the original person, do the following:

JSON.stringify( that.person, function( key, value) {
if( key == 'parent') { return value.id;}
else {return value;}
})

The second parameter to stringify is a filter function. Here it simply converts the referred object to its ID, but you are free to do whatever you like to break the circular reference.

You can test the above code with the following:

function Person( params) {
this.id = params['id'];
this.name = params['name'];
this.father = null;
this.fingers = [];
// etc.
}

var me = new Person({ id: 1, name: 'Luke'});
var him = new Person( { id:2, name: 'Darth Vader'});
me.father = him;
JSON.stringify(me); // so far so good

him.father = me; // time travel assumed :-)
JSON.stringify(me); // "TypeError: Converting circular structure to JSON"
// But this should do the job:
JSON.stringify(me, function( key, value) {
if(key == 'father') {
return value.id;
} else {
return value;
};
});

BTW, I'd choose a different attribute name to "parent" since it is a reserved word in many languages (and in DOM). This tends to cause confusion down the road...

How can I print a circular structure in a JSON-like format?

Use JSON.stringify with a custom replacer. For example:

// Demo: Circular reference
var circ = {};
circ.circ = circ;

// Note: cache should not be re-used by repeated calls to JSON.stringify.
var cache = [];
JSON.stringify(circ, (key, value) => {
if (typeof value === 'object' && value !== null) {
// Duplicate reference found, discard key
if (cache.includes(value)) return;

// Store value in our collection
cache.push(value);
}
return value;
});
cache = null; // Enable garbage collection

The replacer in this example is not 100% correct (depending on your definition of "duplicate"). In the following case, a value is discarded:

var a = {b:1}
var o = {};
o.one = a;
o.two = a;
// one and two point to the same object, but two is discarded:
JSON.stringify(o, ...);

But the concept stands: Use a custom replacer, and keep track of the parsed object values.

As a utility function written in es6:

// safely handles circular references
JSON.safeStringify = (obj, indent = 2) => {
let cache = [];
const retVal = JSON.stringify(
obj,
(key, value) =>
typeof value === "object" && value !== null
? cache.includes(value)
? undefined // Duplicate reference found, discard key
: cache.push(value) && value // Store value in our collection
: value,
indent
);
cache = null;
return retVal;
};

// Example:
console.log('options', JSON.safeStringify(options))

JSON serialize a DOM element

JSON (JavaScript Object Notation) is not designed for serializing DOM Nodes, you'll need to pull out the stuff you want by yourself and write it to an object, and then re-create the DOM Nodes from that if you need.

In fact, Chrome doesn't even execute your code:

TypeError: Converting circular structure to JSON

How to stringify event object?

You won't be able to serialize an event object with JSON.stringify, because an event object contains references to DOM nodes, and the DOM has circular references all over the place (e.g. child/parent relationships). JSON can't handle these by default, so you're a bit out of luck there.

I'd suggest to look at How to serialize DOM node to JSON even if there are circular references? which has a few suggestions on how to serialize a DOM node. Also, the following questions seem to have useful information:

  • How to save an object with circular references?
  • Stringify (convert to JSON) a JavaScript object with circular reference

JSON libraries able to handle circular references seem to be

  • JSON-js (see cycle.js)
  • dojox.json.ref

Alternatively, you could delete all references to DOM nodes if you don't need them, and then serialize the object. You shouldn't do this after all. See @PointedEars comment :)

Why marshaling can serialize circular referenced list and json can't?

In my understanding both of them are converting an object to string and get the object back from the string.

Yes. That is pretty much the definition of "serialization" or "marshaling".

I also able to see that json don't have any construct to refer other part of the json which may be the reason why it can't support this kind of operation.

Yes, that is the reason.

But is it that difficult to introduce such construct in json to facilitate the current situation.

You cannot introduce constructs in JSON. It was deliberately designed to have no version number, so that it can never, ever be changed.

Of course, this only means that we cannot add it now, but could Doug Crockford have added it from the beginning, back when he was designing JSON? Yes, of course. But he didn't. JSON was deliberately designed to be simple (bold emphasis mine):

JSON is not a document format. It is not a markup language. It is not even a general serialization format in that it does not have a direct representation for cyclical structures […]

See, for example, YAML, a superset of JSON, which has references and thus can represent cyclical data.



Related Topics



Leave a reply



Submit