Подтвердить что ты не робот

Использование JavaScript для десериализации ссылок в графе сложных объектов из SignalR/Json.NET

Я использую SignalR для возврата графа сложного объекта к моему клиенту JavaScript. Этот объектный граф имеет множество ссылок на один и тот же объект, поэтому JSON, который возвращает SignalR/Json.NET, выглядит примерно так:

{
    "$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", а затем просто ссылается на этот объект с использованием этого идентификатора (например, "$ref":"57". Насколько я могу судить, когда Json.NET(используя С#/. NET) десериализует эти ссылки, он помещает соответствующие экземпляры объекта в соответствующие места.

Все хорошо до сих пор - но какой лучший способ десериализовать их в JavaScript, чтобы на самом деле получить соответствующие экземпляры объектов в соответствующих местах, а не просто странные поля $ref?

Я мог бы, вероятно, написать свой собственный универсальный десериализатор, но я должен представить, что кто-то еще уже справился с этой проблемой, и я бы не стал изобретать все колеса. К сожалению, моих навыков Google, по-видимому, недостаточно для определения этого решения: -).

Edit:

Я вижу, что существует проект проекта IETF о том, как это должно работать. И похоже, что всегда полезный Douglas Crockford имеет предварительную реализацию. К сожалению, предложение IETF использует другую схему, чем использует Json.NET.

4b9b3361

Ответ 1

Хорошо, я думаю, что это будет сделано. Я изменил 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.