使用JavaScript从SignalR / Json.NET反序列化复杂对象图中的引用

时间:2022-04-28 04:48:26

I'm using SignalR to return a complex object graph to my JavaScript client. This object graph has multiple references throughout to the same object, so the JSON that SignalR/Json.NET returns looks a lot like this:

我正在使用SignalR将复杂的对象图返回给我的JavaScript客户端。这个对象图对同一个对象有多个引用,因此SignalR / Json.NET返回的JSON看起来很像这样:

{
    "$id": "57",
    "Name": "_default",
    "User": {
        "$id": "58",
        "UserTag": "ken",
        "Sessions": [{
            "$id": "59",
            "SessionId": "0ca7474e-273c-4eb2-a0c1-1eba2f1a711c",
            "User": {
                "$ref": "58"
            },
            "Room": {
                "$ref": "57"
            }
        }],
    },

    "Sessions": [{
        "$ref": "59"
    }]
}

(Of course, a lot more complicated in real life, but you get the idea.)

(当然,在现实生活中要复杂得多,但你明白了。)

And of course, when Json.NET is serializing by reference rather than by value, it assigns each object a $id value (e.g., "$id":"57", and then later just refers to that object using that id (e.g., "$ref":"57". And so far as I can tell, when it is Json.NET (using C#/.NET) that is deserializing those references, it places the appropriate instances of the object in the appropriate places.

当然,当Json.NET通过引用而不是值进行序列化时,它会为每个对象分配$ id值(例如,“$ id”:“57”,然后稍后使用该id引用该对象(例如, ,“$ ref”:“57”。据我所知,当Json.NET(使用C#/ .NET)反序列化这些引用时,它会将对象的适当实例放在适当的位置。

All good so far - but what's the best way to deserialize these in JavaScript, so that I actually get the appropriate object instances in the appropriate places, instead of just weird $ref fields?

到目前为止一切都很好 - 但是在JavaScript中反序列化它们的最佳方法是什么,以便我在适当的位置获得适当的对象实例,而不仅仅是奇怪的$ ref字段?

I could presumably write my own general-purpose deserializer, but I have to imagine that somebody else has already tackled this problem, and I'd just as soon not reinvent any wheels. Unfortunately, my Google skills apparently aren't sufficient to locate that solution :-).

我可能会编写自己的通用解串器,但我必须想象其他人已经解决了这个问题,而且我很快就不会重新发明任何*。不幸的是,我的谷歌技能显然不足以找到解决方案:-)。

Edit:

编辑:

I see that there's an IETF draft proposal about how this sort of thing is supposed to work. And it looks like the always helpful Douglas Crockford has a tentative implementation of it. Unfortunately, the IETF proposal uses a different schema than Json.NET uses.

我看到有一个IETF草案提案,关于这类事情应该如何运作。看起来总是有用的道格拉斯克罗克福德有一个暂时的实施。不幸的是,IETF提议使用了与Json.NET使用不同的模式。

1 个解决方案

#1


14  

Well, I think this will do it. I modified Crockford's cycle.js to handle the reference format that Json.NET uses. And because TypeScript is an unspeakably better language than JavaScript, I rewrote it in TS. I certainly don't swear that it has no bugs (if anybody points them out, I'll try to fix 'em), but it seems to handle the complex object graphs I've throw at it so far.

好吧,我认为这样做会。我修改了Crockford的cycle.js来处理Json.NET使用的引用格式。因为TypeScript是一种比JavaScript更难以言说的语言,所以我在TS中重写了它。我当然不发誓它没有错误(如果有人指出它们,我会尝试修复它们),但它似乎处理我迄今为止抛出的复杂对象图。

export function retrocycle(obj: any): void {
    var catalog: any[] = [];
    catalogObject(obj, catalog);
    resolveReferences(obj, catalog);
}

function catalogObject(obj, catalog: any[]):void {

    // The catalogObject function walks recursively through an object graph
    // looking for $id properties. When it finds an object with that property, then
    // it adds it to the catalog under that key.

    var i: number;
    if (obj && typeof obj === 'object') {
        var id:string = obj.$id;
        if (typeof id === 'string') {
            catalog[id] = obj;
        }

        if (Object.prototype.toString.apply(obj) === '[object Array]') {
            for (i = 0; i < obj.length; i += 1) {
                catalogObject(obj[i], catalog);
            }
        } else {
            for (name in obj) {
                if (typeof obj[name] === 'object') {
                    catalogObject(obj[name], catalog);
                }
            }
        }
    }
}

function resolveReferences(obj: any, catalog: any[]) {

    // The resolveReferences function walks recursively through the object looking for $ref
    // properties. When it finds one that has a value that is an id, then it
    // replaces the $ref object with a reference to the object that is found in the catalog under
    // that id.

    var i:number, item:any, name:string, id:string;

    if (obj && typeof obj === 'object') {
        if (Object.prototype.toString.apply(obj) === '[object Array]') {
            for (i = 0; i < obj.length; i += 1) {
                item = obj[i];
                if (item && typeof item === 'object') {
                    id = item.$ref;
                    if (typeof id === 'string') {
                        obj[i] = catalog[id];
                    } else {
                        resolveReferences(item, catalog);
                    }
                }
            }
        } else {
            for (name in obj) {
                if (typeof obj[name] === 'object') {
                    item = obj[name];
                    if (item) {
                        id = item.$ref;
                        if (typeof id === 'string') {
                            obj[name] = catalog[id];
                        } else {
                            resolveReferences(item, catalog);
                        }
                    }
                }
            }
        }
    }
}

And the equivalent JS:

和等效的JS:

function retrocycle(obj) {
    var catalog = [];
    catalogObject(obj, catalog);
    resolveReferences(obj, catalog);
}

function catalogObject(obj, catalog) {
    var i;
    if (obj && typeof obj === 'object') {
        var id = obj.$id;
        if (typeof id === 'string') {
            catalog[id] = obj;
        }
        if (Object.prototype.toString.apply(obj) === '[object Array]') {
            for (i = 0; i < obj.length; i += 1) {
                catalogObject(obj[i], catalog);
            }
        } else {
            for (name in obj) {
                if (typeof obj[name] === 'object') {
                    catalogObject(obj[name], catalog);
                }
            }
        }
    }
}

function resolveReferences(obj, catalog) {
    var i, item, name, id;
    if (obj && typeof obj === 'object') {
        if (Object.prototype.toString.apply(obj) === '[object Array]') {
            for (i = 0; i < obj.length; i += 1) {
                item = obj[i];
                if (item && typeof item === 'object') {
                    id = item.$ref;
                    if (typeof id === 'string') {
                        obj[i] = catalog[id];
                    } else {
                        resolveReferences(item, catalog);
                    }
                }
            }
        } else {
            for (name in obj) {
                if (typeof obj[name] === 'object') {
                    item = obj[name];
                    if (item) {
                        id = item.$ref;
                        if (typeof id === 'string') {
                            obj[name] = catalog[id];
                        } else {
                            resolveReferences(item, catalog);
                        }
                    }
                }
            }
        }
    }
}

You use it kinda like so (assuming you've got SignalR hubs wired up):

你使用它有点像(假设你有SignalR集线器连线):

$.connection.roomHub.server.joinRoom()
    .done(function(room) {
        retrocycle(room);
    });

I also created a quick-and-dirty little repository out on BitBucket for it: https://bitbucket.org/smithkl42/jsonnetdecycle.

我还在BitBucket上为它创建了一个快速而又脏的小存储库:https://bitbucket.org/smithkl42/jsonnetdecycle。

#1


14  

Well, I think this will do it. I modified Crockford's cycle.js to handle the reference format that Json.NET uses. And because TypeScript is an unspeakably better language than JavaScript, I rewrote it in TS. I certainly don't swear that it has no bugs (if anybody points them out, I'll try to fix 'em), but it seems to handle the complex object graphs I've throw at it so far.

好吧,我认为这样做会。我修改了Crockford的cycle.js来处理Json.NET使用的引用格式。因为TypeScript是一种比JavaScript更难以言说的语言,所以我在TS中重写了它。我当然不发誓它没有错误(如果有人指出它们,我会尝试修复它们),但它似乎处理我迄今为止抛出的复杂对象图。

export function retrocycle(obj: any): void {
    var catalog: any[] = [];
    catalogObject(obj, catalog);
    resolveReferences(obj, catalog);
}

function catalogObject(obj, catalog: any[]):void {

    // The catalogObject function walks recursively through an object graph
    // looking for $id properties. When it finds an object with that property, then
    // it adds it to the catalog under that key.

    var i: number;
    if (obj && typeof obj === 'object') {
        var id:string = obj.$id;
        if (typeof id === 'string') {
            catalog[id] = obj;
        }

        if (Object.prototype.toString.apply(obj) === '[object Array]') {
            for (i = 0; i < obj.length; i += 1) {
                catalogObject(obj[i], catalog);
            }
        } else {
            for (name in obj) {
                if (typeof obj[name] === 'object') {
                    catalogObject(obj[name], catalog);
                }
            }
        }
    }
}

function resolveReferences(obj: any, catalog: any[]) {

    // The resolveReferences function walks recursively through the object looking for $ref
    // properties. When it finds one that has a value that is an id, then it
    // replaces the $ref object with a reference to the object that is found in the catalog under
    // that id.

    var i:number, item:any, name:string, id:string;

    if (obj && typeof obj === 'object') {
        if (Object.prototype.toString.apply(obj) === '[object Array]') {
            for (i = 0; i < obj.length; i += 1) {
                item = obj[i];
                if (item && typeof item === 'object') {
                    id = item.$ref;
                    if (typeof id === 'string') {
                        obj[i] = catalog[id];
                    } else {
                        resolveReferences(item, catalog);
                    }
                }
            }
        } else {
            for (name in obj) {
                if (typeof obj[name] === 'object') {
                    item = obj[name];
                    if (item) {
                        id = item.$ref;
                        if (typeof id === 'string') {
                            obj[name] = catalog[id];
                        } else {
                            resolveReferences(item, catalog);
                        }
                    }
                }
            }
        }
    }
}

And the equivalent JS:

和等效的JS:

function retrocycle(obj) {
    var catalog = [];
    catalogObject(obj, catalog);
    resolveReferences(obj, catalog);
}

function catalogObject(obj, catalog) {
    var i;
    if (obj && typeof obj === 'object') {
        var id = obj.$id;
        if (typeof id === 'string') {
            catalog[id] = obj;
        }
        if (Object.prototype.toString.apply(obj) === '[object Array]') {
            for (i = 0; i < obj.length; i += 1) {
                catalogObject(obj[i], catalog);
            }
        } else {
            for (name in obj) {
                if (typeof obj[name] === 'object') {
                    catalogObject(obj[name], catalog);
                }
            }
        }
    }
}

function resolveReferences(obj, catalog) {
    var i, item, name, id;
    if (obj && typeof obj === 'object') {
        if (Object.prototype.toString.apply(obj) === '[object Array]') {
            for (i = 0; i < obj.length; i += 1) {
                item = obj[i];
                if (item && typeof item === 'object') {
                    id = item.$ref;
                    if (typeof id === 'string') {
                        obj[i] = catalog[id];
                    } else {
                        resolveReferences(item, catalog);
                    }
                }
            }
        } else {
            for (name in obj) {
                if (typeof obj[name] === 'object') {
                    item = obj[name];
                    if (item) {
                        id = item.$ref;
                        if (typeof id === 'string') {
                            obj[name] = catalog[id];
                        } else {
                            resolveReferences(item, catalog);
                        }
                    }
                }
            }
        }
    }
}

You use it kinda like so (assuming you've got SignalR hubs wired up):

你使用它有点像(假设你有SignalR集线器连线):

$.connection.roomHub.server.joinRoom()
    .done(function(room) {
        retrocycle(room);
    });

I also created a quick-and-dirty little repository out on BitBucket for it: https://bitbucket.org/smithkl42/jsonnetdecycle.

我还在BitBucket上为它创建了一个快速而又脏的小存储库:https://bitbucket.org/smithkl42/jsonnetdecycle。