15

如果我有来自 json.net 的序列化 JSON,如下所示:

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

我想在 FooList 上进行淘汰赛输出,但我不确定如何继续,因为 $ref 东西可能会抛出东西。

我认为解决方案是通过不使用以某种方式强制将所有 Foos 呈现在 FooList 中:

PreserveReferencesHandling = PreserveReferencesHandling.Objects

但这似乎很浪费..

4

6 回答 6

28

我发现了一些错误并实现了数组支持:

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 (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 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[ref[2]];
        // Notice that this throws if you put in a reference at top-level
    }
    return json;
}
于 2013-04-02T06:25:20.917 回答
14

您从服务器接收的 json 对象包含Circular References。在使用对象之前,您必须首先$ref从对象中删除所有属性,这意味着$ref : "1"您必须放置此链接指向的对象。

在您的情况下,它可能指向 id 为 1 的用户对象

为此,您应该查看github 上的 Douglas Crockfords 插件。有一个 cycle.js 可以为您完成这项工作。

或者您可以使用以下代码(未经测试):

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;
}  

让我知道它是否有帮助!

于 2013-03-11T08:19:58.783 回答
13

如果您利用JSON.parse'reviver参数,这实际上非常简单。

下面的例子。请参阅浏览器控制台以获取输出,因为 StackOverflow 的片段控制台输出不会提供结果的准确图片。

// example JSON
var j = '{"$id":"0","name":"Parent","child":{"$id":"1", "name":"Child","parent":{"$ref":"0"}},"nullValue":null}'

function parseAndResolve(json) {
    var refMap = {};

    return JSON.parse(json, function (key, value) {
        if (key === '$id') { 
            refMap[value] = this;
            // return undefined so that the property is deleted
            return void(0);
        }

        if (value && value.$ref) { return refMap[value.$ref]; }

        return value; 
    });
}

console.log(parseAndResolve(j));

于 2017-02-22T17:53:50.010 回答
3

我在 Alexander Vasiliev 的回答中遇到了阵列校正问题。

我无法评论他的答案(没有足够的声誉点;-)),所以我不得不添加一个新的答案......(我有一个弹出窗口作为最佳做法,不回答其他答案,只回答原始问题-bof)

    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
                return obj[i];
            if ("$ref" in obj[i])
                obj[i] = recurse(obj[i], i, obj);
            else
                obj[i] = recurse(obj[i], prop, obj);
        }
        return obj;
    }
于 2013-11-18T15:04:06.483 回答
2

在公认的实现中,如果您正在检查一个数组并遇到一个原始值,您将返回该值并覆盖该数组。相反,您希望继续检查数组的所有元素并在最后返回数组。

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 (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 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[ref[2]];
        // Notice that this throws if you put in a reference at top-level
    }
    return json;
}
于 2015-05-21T21:02:59.667 回答
1

我的解决方案(也适用于数组):

用法:rebuildJsonDotNetObj(jsonDotNetResponse)

编码:

function rebuildJsonDotNetObj(obj) {
    var arr = [];
    buildRefArray(obj, arr);
    return setReferences(obj, arr)
}

function buildRefArray(obj, arr) {
    if (!obj || obj['$ref'])
        return;
    var objId = obj['$id'];
    if (!objId)
    {
        obj['$id'] = "x";
        return;
    }
    var id = parseInt(objId);
    var array = obj['$values'];
    if (array && Array.isArray(array)) {
        arr[id] = array;
        array.forEach(function (elem) {
            if (typeof elem === "object")
                buildRefArray(elem, arr);
        });
    }
    else {
        arr[id] = obj;
        for (var prop in obj) {
            if (typeof obj[prop] === "object") {
                buildRefArray(obj[prop], arr);
            }
        }
    }
}

function setReferences(obj, arrRefs) {
    if (!obj)
        return obj;
    var ref = obj['$ref'];
    if (ref)
        return arrRefs[parseInt(ref)];

    if (!obj['$id']) //already visited
        return obj;

    var array = obj['$values'];
    if (array && Array.isArray(array)) {
        for (var i = 0; i < array.length; ++i)
            array[i] = setReferences(array[i], arrRefs)
        return array;
    }
    for (var prop in obj)
        if (typeof obj[prop] === "object")
            obj[prop] = setReferences(obj[prop], arrRefs)
    delete obj['$id'];
    return obj;
}
于 2016-02-05T00:35:40.997 回答