13

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を使用)の場合、オブジェクトの適切なインスタンスを適切な場所に配置します。

これまでのところすべて良好ですが、JavaScriptでこれらを逆シリアル化して、奇妙な$ refフィールドではなく、適切な場所で適切なオブジェクトインスタンスを実際に取得するための最良の方法は何ですか?

私はおそらく自分の汎用デシリアライザーを書くことができましたが、他の誰かがすでにこの問題に取り組んでいることを想像する必要があり、私はすぐに車輪を再発明しません。残念ながら、私のグーグルのスキルは明らかにその解決策を見つけるのに十分ではありません:-)。

編集:

この種のものがどのように機能するかについてのIETFドラフト提案があるようです。そして、常に役立つダグラス・クロックフォードが暫定的に実装しているようです。残念ながら、IETF提案はJson.NETが使用するものとは異なるスキーマを使用します。

4

1 に答える 1

16

まあ、これでうまくいくと思います。Json.NETが使用する参照形式を処理するようにCrockfordのcycle.jsを変更しました。また、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

于 2012-12-08T23:54:02.540 に答える