web-dev-qa-db-ja.com

JSONからTypeScriptクラスインスタンスへ?

私はかなりの研究をしましたが、私が見つけたものに完全に満足していません。念のため、ここに私の質問があります:実際にJSONをTypeScriptランタイムクラスインスタンスにデシリアライズするための最も堅牢でエレガントな自動化ソリューションは何ですか?

このクラスを取得したとしましょう:

class Foo {
  name: string;
  GetName(): string { return this.name };
}

そして、逆シリアル化のためにこのJSON文字列を取得したとしましょう:

{"name": "John Doe"}

名前が「John Doe」に設定され、メソッドGetName()が機能するFooクラスのインスタンスを取得するための最良かつ最も保守可能なソリューションは何ですか?純粋なデータオブジェクトに簡単に逆シリアル化できることを知っているので、非常に具体的に尋ねています。手動の解析や手動のデータコピーを行わずに、作業メソッドを使用してクラスインスタンスを取得できるかどうか疑問に思っています。完全に自動化されたソリューションが不可能な場合、次に最適なソリューションは何ですか?

86
Klaus

この質問は非常に広いので、いくつかの解決策を示します。

解決策1:ヘルパーメソッド

必要に応じて変更できるヘルパーメソッドの使用例を次に示します。

class SerializationHelper {
    static toInstance<T>(obj: T, json: string) : T {
        var jsonObj = JSON.parse(json);

        if (typeof obj["fromJSON"] === "function") {
            obj["fromJSON"](jsonObj);
        }
        else {
            for (var propName in jsonObj) {
                obj[propName] = jsonObj[propName]
            }
        }

        return obj;
    }
}

それを使用して:

var json = '{"name": "John Doe"}',
    foo = SerializationHelper.toInstance(new Foo(), json);

foo.GetName() === "John Doe";

高度な逆シリアル化

これにより、独自のfromJSONメソッドをクラスに追加することにより、カスタムの逆シリアル化が可能になります(これは、以下に示すように、JSON.stringifyがすでにtoJSONメソッドを使用する方法でうまく機能します)。

interface IFooSerialized {
    nameSomethingElse: string;
}

class Foo {
  name: string;
  GetName(): string { return this.name }

  toJSON(): IFooSerialized {
      return {
          nameSomethingElse: this.name
      };
  }

  fromJSON(obj: IFooSerialized) {
        this.name = obj.nameSomethingElse;
  }
}

それを使用して:

var foo1 = new Foo();
foo1.name = "John Doe";

var json = JSON.stringify(foo1);

json === '{"nameSomethingElse":"John Doe"}';

var foo2 = SerializationHelper.toInstance(new Foo(), json);

foo2.GetName() === "John Doe";

解決策2:基本クラス

これを行う別の方法は、独自の基本クラスを作成することです。

class Serializable {
    fillFromJSON(json: string) {
        var jsonObj = JSON.parse(json);
        for (var propName in jsonObj) {
            this[propName] = jsonObj[propName]
        }
    }
}

class Foo extends Serializable {
    name: string;
    GetName(): string { return this.name }
}

それを使用して:

var foo = new Foo();
foo.fillFromJSON(json);

基本クラスを使用してカスタムの逆シリアル化を実装する方法は多すぎるため、必要に応じてそれを残します。

67
David Sherret

これでObject.assign(target, ...sources)を使用できます。あなたの例に従って、次のように使用できます:

class Foo {
  name: string;
  getName(): string { return this.name };
}

let fooJson: string = '{"name": "John Doe"}';
let foo: Foo = Object.assign(new Foo(), JSON.parse(fooJson));

console.log(foo.getName()); //returns John Doe

Object.assignECMAScript 2015 の一部であり、現在ほとんどの最新ブラウザーで利用可能です。

43
Hugo Leao

JSONをTypeScriptランタイムクラスインスタンスに逆シリアル化するための実際に最も堅牢でエレガントな自動化ソリューションは何ですか?

プロパティデコレータReflectDecorators とともに使用して、デシリアライズプロセス中に使用できるランタイムアクセス可能な型情報を記録すると、驚くほどクリーンで幅広く適応可能なアプローチ。既存のコードにも美しく適合します。また、完全に自動化可能で、ネストされたオブジェクトでも機能します。

このアイデアの実装は TypedJSON で、このタスクのために正確に作成しました:

@JsonObject
class Foo {
    @JsonMember
    name: string;

    getName(): string { return this.name };
}
var foo = TypedJSON.parse('{"name": "John Doe"}', Foo);

foo instanceof Foo; // true
foo.getName(); // "John Doe"
13
John Weisz

なぜあなたはこのようなことをできないのですか?

class Foo {
  constructor(myObj){
     Object.assign(this, myObj);
  }
  get name() { return this._name; }
  set name(v) { this._name = v; }
}

let foo = new Foo({ name: "bat" });
foo.toJSON() //=> your json ...
4
amcdnl