`SolveReferences` not working properly

Hey,
I use RawInvoke to get a TList<> object from an XData service as a return from a function, e.g. function GetValues: TList<TData>. In the WebCore application, I work with the list as a TJSArray object. If I want to get an object out of the list, I can do that, but when I want to get the same object a second time (for example, within a loop), I only get a reference ($ref) to the object and not the object itself.
I saw that there is a procedure called TXDataWebClient.SolveReferences(json: JSValue); that is supposed to solve the reference so I can get the object. When looking into it, I noticed that there is an if statement that checks if the object is an object Array. Inside this if statement, it solves the reference for each object inside the array.
Now we get to the problem. My TList object has a value that includes the array, but since it only checks if the TList object is an object Array, which returns false for the if statement, it does not solve any references for the objects inside my list.
Is this a mistake on my end and am I doing something wrong, or could it be that this is a bug?

This procedure is included in the XData.Web.Client.pas

Thanks for your reply.

1 Like

Dear @Winkel_Philipp1,

Indeed it looks like a bug, and thank you for the investigation and detailed report. We will fix this for the next release.

In the meanwhile, this is the updated function we will release. If you feel like patching your source code right away, please let me know if it works for you:

procedure TXDataWebClient.SolveReferences(json: JSValue);
asm
    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 (Object.prototype.toString.call(obj) === '[object Array]') {
            for (var i = 0; i < obj.length; i++)
                // check also if the array element is not a primitive value
                if (typeof obj[i] !== 'object' || !obj[i]) // a primitive value
                    continue;
                else if ("$ref" in obj[i])
                    obj[i] = recurse(obj[i], i, obj);
                else
                    obj[i] = recurse(obj[i], prop, obj);
            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 p in obj)
                    obj[p] = recurse(obj[p], p, obj);
            byid[id] = obj;
        } else {
            // NEW: recurse into plain objects (no $id / $ref),
            // e.g. { "value": [ ... ] }
            for (var p in obj)
                obj[p] = recurse(obj[p], p, 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[ref[2]];
        // Notice that this throws if you put in a reference at top-level
    }
end;

Dear Wagner Landgraf,
I patched my source code with your altered procedure and tested it. I’m now getting the actual objects instead of just the references. This fixes the problem I had with my generic TList.
Thank you for your help.

Do you know the release date for this patch?

We don't have a timeframe yet.