Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to find the circular structure in JSON.stringify: Uncaught TypeError: Converting circular structure to JSON?

When I get Uncaught TypeError: Converting circular structure to JSON on a large structure it can be very difficult to find out where exactly the circular reference is.

Is there a simple way to find/debug the circular element in the data structure?

like image 715
Michael_Scharf Avatar asked Jan 06 '14 15:01

Michael_Scharf


People also ask

How do you convert circular structure to string?

A circular structure is an object that references itself. To be able to stringify such objects, developers can utilize the replacer parameter in the stringify() method, making sure the function that is being passed in, filters out repeated or circular data.

What is the circular structure?

The circular structure is when you try to reference an object which directly or indirectly references itself. Example: A -> B -> A OR A -> A.

What is circular reference in JavaScript?

A circular reference occurs if two separate objects pass references to each other. In older browsers circular references were a cause of memory leaks. With improvements in Garbage collection algorithms, which can now handle cycles and cyclic dependencies fine, this is no longer an issue.


1 Answers

I haven't found a simple method to do it, other people seem to be suggesting using custom replacer function in JSON.stringify to control which properties have been visited.

I've attempted to write such replacer:

function detector(obj) {
    function collector (stack, key, val) {
        var idx = stack[stack.length - 1].indexOf(key);

        try {
            var props = Object.keys(val);
            if (!props.length) throw props;
            props.unshift({idx : idx});
            stack.push(props);
        } catch (e) {
            while (!(stack[stack.length - 1].length - 2)) {
                idx = stack[stack.length -1][0].idx;
                stack.pop();
            }

            if (idx + 1) {
                stack[stack.length - 1].splice(idx, 1);
            }
        }

        return val;
    }

    var stack = [[]];

    try {
        JSON.stringify(obj, collector.bind(null, stack));
    } catch (e) {
        if (e.message.indexOf('circular') !== -1) {
            var idx = 0;
            var path = '';
            var parentProp = '';
            while(idx + 1) {
                idx = stack.pop()[0].idx;
                parentProp = stack[stack.length - 1][idx];
                if (!parentProp) break;
                path = '.' + parentProp + path;
            }

            console.log(path);
        }
    }
}

What it does is while traversing the JSON tree (probably tree :)) it collects names of properties which have been visited and as soon as JSON.stringify detects circular reference and throws, 'stack' variable will contain a trace of which subtree it was traversing. And it logs this path to console.

However, this is not a heavily tested solution.

like image 103
t.O Avatar answered Sep 28 '22 04:09

t.O