Search code examples
javascriptjqueryknockout.jsjson.net

Resolve circular references from JSON object


If I have a serialized JSON from json.net like so:

User:{id:1,{Foo{id:1,prop:1}},
FooList{$ref: "1",Foo{id:2,prop:13}}

I want to have knockout output a foreach over FooList but I am not sure how to proceed because the $ref things could throw things.

I'm thinking the solution would be to somehow force all the Foos to be rendered in the FooList by not using:

PreserveReferencesHandling = PreserveReferencesHandling.Objects

but that seems wasteful..


Solution

  • The json object which you are receiving from the server contains Circular References. Before using the object you should have to first remove all the $ref properties from the object, means in place of $ref : "1" you have to put the object which this link points.

    In your case may be it is pointing to the User's object whose id is 1

    For this you should check out Douglas Crockfords Plugin on github.There is a cycle.js which can do the job for you.

    or you can use the following code (not tested) :

    function resolveReferences(json) {
        if (typeof json === 'string')
            json = JSON.parse(json);
    
        var byid = {}, // all objects by id
            refs = []; // references to objects that could not be resolved
        json = (function recurse(obj, prop, parent) {
            if (typeof obj !== 'object' || !obj) // a primitive value
                return obj;
            if ("$ref" in obj) { // a reference
                var ref = obj.$ref;
                if (ref in byid)
                    return byid[ref];
                // else we have to make it lazy:
                refs.push([parent, prop, ref]);
                return;
            } else if ("$id" in obj) {
                var id = obj.$id;
                delete obj.$id;
                if ("$values" in obj) // an array
                    obj = obj.$values.map(recurse);
                else // a plain object
                    for (var prop in obj)
                        obj[prop] = recurse(obj[prop], prop, obj)
                byid[id] = obj;
            }
            return obj;
        })(json); // run it!
    
        for (var i=0; i<refs.length; i++) { // resolve previously unknown references
            var ref = refs[i];
            ref[0][ref[1]] = byid[refs[2]];
            // Notice that this throws if you put in a reference at top-level
        }
        return json;
    }  
    

    Let me know if it helps !