我正在使用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" }] }
(当然,现实生活中的事情要复杂得多,但您会明白的。)
当然,当Json.NET通过引用而不是按值进行序列化时,它将为每个对象分配一个$ id值(例如,"$id":"57"然后再使用该ID引用该对象(例如"$ref":"57"。),据我所知当Json.NET(使用C#/。NET)反序列化这些引用时,它会将对象的适当实例放置在适当的位置。
"$id":"57"
"$ref":"57"
到目前为止一切都很好-但是 在JavaScript中 反序列化这些内容的最佳方法是什么,以便实际上在适当的位置获得适当的对象实例,而不仅仅是奇怪的$ ref字段?
我大概可以编写自己的通用解串器,但是我必须想象其他人已经解决了这个问题,并且我会尽快不发明任何轮子。不幸的是,我的Google技能显然不足以找到该解决方案:-)。
编辑:
我看到有一个IETF提案草案,关于这种事情应该如何工作。看起来总是有用的道格拉斯·克罗克福德(Douglas Crockford)尝试了它。不幸的是,IETF提案使用的模式与Json.NET使用的模式不同。
好吧,我认为这可以做到。我修改了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); } } } } } } }
和等效的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); } } } } } } }
您可以像这样使用它(假设您已连接SignalR集线器):
$.connection.roomHub.server.joinRoom() .done(function(room) { retrocycle(room); });
我还为此在BitBucket上创建了一个快速且肮脏的小存储库: https://bitbucket.org/smithkl42/jsonnetdecycle。