TypeScriptオブジェクトをJSONオブジェクトで初期化する方法


198

RESTサーバーへのAJAX呼び出しからJSONオブジェクトを受け取ります。このオブジェクトには、TypeScriptクラスに一致するプロパティ名があります(これは、この質問の続きです)。

それを初期化する最良の方法は何ですか?クラス(およびJSONオブジェクト)にはオブジェクトのリストであるメンバーとクラスであるメンバーがあり、それらのクラスにはリストとクラスの両方またはいずれか一方のメンバーがあるため、これはうまくいきません。

しかし、私はメンバー名を調べてそれらを全体に割り当て、リストを作成し、必要に応じてクラスをインスタンス化するアプローチを好みます。そのため、すべてのクラスのすべてのメンバーに対して明示的なコードを書く必要はありません(LOTがあります!)


1
なぜあなたはこれをもう一度尋ねたのですか(他の質問で私が提供した答えはこれはうまくいかず、プロパティを既存のオブジェクトにコピーすることに関するものだったので)?
WiredPrairie 2014


3
@WiredPrairieこの質問は異なります。プロパティを1つずつ調べて、全体に割り当てることができるかどうかを尋ねています。他の質問は私がそれをキャストできるかどうか尋ねていました。
David Thielen、2014

1
@WiredPrairie cont:プリミティブ型だけに到達するまでプロパティに飛び込む場合、それらを全体に割り当てることができます。
David Thielen、2014

2
私があなたがする必要があると示唆したように、それはまだすべての値をコピーしています。TypeScriptはJavaScriptの基本的な設計であるため、これを行う新しい方法はありません。ラージオブジェクトの場合、値をコピーせずに、データ構造を「操作」するだけです。
WiredPrairie 14

回答:


188

これらは、いくつかの異なる方法を示すための、ここでのいくつかのクイックショットです。それらは決して「完全」ではなく、免責事項として、私はこのようにすることは良い考えではないと思います。また、コードをすぐに一緒に入力しただけなので、コードはあまりクリーンではありません。

また、メモとして:もちろん、逆シリアル化可能なクラスには、あらゆる種類の逆シリアル化を知っている他のすべての言語の場合と同様に、デフォルトのコンストラクターが必要です。もちろん、引数なしでデフォルト以外のコンストラクターを呼び出しても、Javascriptは文句を言わないでしょうが、クラスはそのために準備された方がいいです(さらに、それは実際には「タイプスクリプトの方法」ではありません)。

オプション#1:実行時情報がまったくない

このアプローチの問題は、ほとんどの場合、メンバーの名前がそのクラスと一致する必要があることです。これにより、クラスごとに同じタイプの1つのメンバーに自動的に制限され、優れた実践のいくつかのルールに違反します。私はこれに対して強くお勧めしますが、この回答を書いたときの最初の「ドラフト」だったので、ここにリストしてください(これは、名前が「Foo」などである理由でもあります)。

module Environment {
    export class Sub {
        id: number;
    }

    export class Foo {
        baz: number;
        Sub: Sub;
    }
}

function deserialize(json, environment, clazz) {
    var instance = new clazz();
    for(var prop in json) {
        if(!json.hasOwnProperty(prop)) {
            continue;
        }

        if(typeof json[prop] === 'object') {
            instance[prop] = deserialize(json[prop], environment, environment[prop]);
        } else {
            instance[prop] = json[prop];
        }
    }

    return instance;
}

var json = {
    baz: 42,
    Sub: {
        id: 1337
    }
};

var instance = deserialize(json, Environment, Environment.Foo);
console.log(instance);

オプション#2:nameプロパティ

オプション#1の問題を取り除くには、JSONオブジェクトのノードのタイプに関する何らかの情報が必要です。問題は、Typescriptでは、これらはコンパイル時の構成であり、実行時に必要になるということですが、実行時オブジェクトは、設定されるまでプロパティを認識しません。

これを行う1つの方法は、クラスに名前を認識させることです。ただし、JSONにもこのプロパティが必要です。実際には、jsonでのみ必要です。

module Environment {
    export class Member {
        private __name__ = "Member";
        id: number;
    }

    export class ExampleClass {
        private __name__ = "ExampleClass";

        mainId: number;
        firstMember: Member;
        secondMember: Member;
    }
}

function deserialize(json, environment) {
    var instance = new environment[json.__name__]();
    for(var prop in json) {
        if(!json.hasOwnProperty(prop)) {
            continue;
        }

        if(typeof json[prop] === 'object') {
            instance[prop] = deserialize(json[prop], environment);
        } else {
            instance[prop] = json[prop];
        }
    }

    return instance;
}

var json = {
    __name__: "ExampleClass",
    mainId: 42,
    firstMember: {
        __name__: "Member",
        id: 1337
    },
    secondMember: {
        __name__: "Member",
        id: -1
    }
};

var instance = deserialize(json, Environment);
console.log(instance);

オプション#3:メンバーのタイプを明示的に述べる

上記のように、クラスメンバーの型情報は実行時には利用できません。つまり、利用できるようにしない限りです。これを行う必要があるのは、プリミティブ以外のメンバーだけです。

interface Deserializable {
    getTypes(): Object;
}

class Member implements Deserializable {
    id: number;

    getTypes() {
        // since the only member, id, is primitive, we don't need to
        // return anything here
        return {};
    }
}

class ExampleClass implements Deserializable {
    mainId: number;
    firstMember: Member;
    secondMember: Member;

    getTypes() {
        return {
            // this is the duplication so that we have
            // run-time type information :/
            firstMember: Member,
            secondMember: Member
        };
    }
}

function deserialize(json, clazz) {
    var instance = new clazz(),
        types = instance.getTypes();

    for(var prop in json) {
        if(!json.hasOwnProperty(prop)) {
            continue;
        }

        if(typeof json[prop] === 'object') {
            instance[prop] = deserialize(json[prop], types[prop]);
        } else {
            instance[prop] = json[prop];
        }
    }

    return instance;
}

var json = {
    mainId: 42,
    firstMember: {
        id: 1337
    },
    secondMember: {
        id: -1
    }
};

var instance = deserialize(json, ExampleClass);
console.log(instance);

オプション#4:冗長ですが適切な方法

2016年1月3日更新: @GameAlchemistがコメント(アイデア実装)で指摘したように、Typescript 1.7以降では、クラス/プロパティデコレーターを使用して、以下で説明するソリューションをより適切に記述できます。

シリアライゼーションは常に問題であり、私の意見では、最善の方法は最短ではない方法です。クラスの作成者がデシリアライズされたオブジェクトの状態を完全に制御できるため、すべてのオプションのうち、これが私が好むものです。私が推測しなければならなかった場合、遅かれ早かれ他のすべてのオプションで問題が発生すると思います(Javascriptがこれに対処するためのネイティブな方法を考え出さない限り)。

実際、次の例は柔軟性の正義を行いません。実際には、クラスの構造をコピーするだけです。ただし、ここで注意しなければならない違いは、クラスがクラス全体の状態を制御したいあらゆる種類のJSONを使用するためのフルコントロールを持っていることです(計算など)。

interface Serializable<T> {
    deserialize(input: Object): T;
}

class Member implements Serializable<Member> {
    id: number;

    deserialize(input) {
        this.id = input.id;
        return this;
    }
}

class ExampleClass implements Serializable<ExampleClass> {
    mainId: number;
    firstMember: Member;
    secondMember: Member;

    deserialize(input) {
        this.mainId = input.mainId;

        this.firstMember = new Member().deserialize(input.firstMember);
        this.secondMember = new Member().deserialize(input.secondMember);

        return this;
    }
}

var json = {
    mainId: 42,
    firstMember: {
        id: 1337
    },
    secondMember: {
        id: -1
    }
};

var instance = new ExampleClass().deserialize(json);
console.log(instance);

12
オプション#4は、私が妥当な方法と呼んでいるものです。逆シリアル化コードを記述する必要がありますが、同じクラスにあり、完全に制御可能です。Javaを使用している場合、これはequalstoStringメソッドまたはメソッドを作成する必要があることに相当します(通常、それらは自動生成されます)。必要に応じてジェネレータを作成することはそれほど難しくありdeserializeませんが、実行時の自動化にはなりません。
IngoBürk14年

2
@IngoBürk、私は2年後にこの質問をしているのを知っていますが、これはオブジェクトの配列でどのように機能しますか?上記のサンプルコードは、JSONオブジェクトに対して正常に機能します。オブジェクトの配列にどのように使用できますか?
Pratik Gaikwad

2
補足:1.7以降(確かに回答よりも新しい)、typescriptはクラス/プロパティデコレータを提供して、4番目のソリューションをより簡潔に記述できるようにします。
GameAlchemist 2016年

1
私が見つけた最良のドキュメントはStackOverflowの回答です:stackoverflow.com/a/29837695/856501 。私は私のプロジェクトでデコレータを使用しました。他のいくつかの機能が欲しいのですが、それらは魅力のように機能すると言わざるを得ません。
GameAlchemist 2016

2
私はまだプロダクションプロジェクトのデコレーターにジャンプするつもりはありません-それらはまだ実験的な機能であることを覚えておいてください。実際のコードは「実験」に基づいたものにはしません。なぜなら、私たちが懸念している限り、それらは次のバージョンでなくなる可能性があり、一連のコードを書き直さなければならないか、古いTSバージョンに永遠に行き詰まってしまうからです。ちょうど私の$ .02
RVP

35

あなたが使用することができObject.assign、これが追加されたとき、私は知らない、私は現在、活字体2.0.2を使用していますが、これはES6の特徴であると思われます。

client.fetch( '' ).then( response => {
        return response.json();
    } ).then( json => {
        let hal : HalJson = Object.assign( new HalJson(), json );
        log.debug( "json", hal );

ここに HalJson

export class HalJson {
    _links: HalLinks;
}

export class HalLinks implements Links {
}

export interface Links {
    readonly [text: string]: Link;
}

export interface Link {
    readonly href: URL;
}

これはクロムが言っていることです

HalJson {_links: Object}
_links
:
Object
public
:
Object
href
:
"http://localhost:9000/v0/public

再帰的に割り当てを行わないことがわかります


2
つまり、基本的には次のようになりますObject.assign。では、なぜこの上に2つのレキシコンのような答えがあるのでしょうか。
phil294 '18

18
@Blauhim Object.assignは、再帰的に機能せず、正しいオブジェクトタイプをインスタンス化せず、値をObjectインスタンスとして残します。ささいなタスクには問題ありませんが、複雑な型のシリアル化はできません。たとえば、クラスプロパティがカスタムクラスタイプの場合、JSON.parse+ Object.assignはそのプロパティをにインスタンス化しますObject。副作用には、欠落しているメソッドとアクセサーが含まれます。
John Weisz 2017年

@JohnWeiszオブジェクト割り当ての最上位クラスには正しいタイプがあり、これについて再帰的なことを述べました...つまり、YMMV、そしてそれらは取引ブレーカーである可能性があります。
xenoterracide 2017年

質問から直接引用:「クラスにはオブジェクトのリストであるメンバーとクラスであるメンバーがあり、それらのクラスにはリストとクラスの両方またはいずれかであるメンバーがあります[...]メンバーを検索するアプローチが望ましい名前および譲受人それらを、リストを作成し、必要に応じてクラスをインスタンス化し、全体で私はすべてのクラスのすべてのメンバーの明示的なコードを記述する必要はありませんので、 " -とそうではありませんこれはObject.assign、それはまだで、ネストされたインスタンス化を書くことに帰着手。このアプローチは、非常に単純なチュートリアルレベルのオブジェクトには適していますが、実際には使用できません。
John Weisz 2017年

@JohnWeisz確かに、ほとんどの場合これで答えられました。これは回答に含まれておらず、一部のユースケースでは単純に見えたためです。あなたが探していることをするために、それを反射などの他の答えと組み合わせて使用​​することもできると私は確信しています。あとで覚えられるように一部書きました。これらの回答を見て、より強力なライブラリを使用および作成したことで、「実際に使用」できるものが何もないようです。
xenoterracide 2017年

34

TLDR:TypedJSON(実用的な概念実証)


この問題の複雑さの原因は、コンパイル時にのみ存在する型情報を使用して、実行時に JSONをデシリアライズする必要があることです。これには、型情報が実行時に何らかの形で利用可能になることが必要です。

幸い、これはデコレータReflectDecoratorsを使用して非常にエレガントで堅牢な方法で解決できます。

  1. シリアル化の対象となるプロパティでプロパティデコレータを使用して、メタデータ情報を記録し、その情報をどこかに、たとえばクラスプロトタイプに格納します。
  2. このメタデータ情報を再帰的なイニシャライザ(デシリアライザ)にフィードする

 

記録タイプ-情報

ReflectDecoratorsとプロパティデコレーターの組み合わせにより、プロパティに関する型情報を簡単に記録できます。このアプローチの初歩的な実装は次のようになります。

function JsonMember(target: any, propertyKey: string) {
    var metadataFieldKey = "__propertyTypes__";

    // Get the already recorded type-information from target, or create
    // empty object if this is the first property.
    var propertyTypes = target[metadataFieldKey] || (target[metadataFieldKey] = {});

    // Get the constructor reference of the current property.
    // This is provided by TypeScript, built-in (make sure to enable emit
    // decorator metadata).
    propertyTypes[propertyKey] = Reflect.getMetadata("design:type", target, propertyKey);
}

任意の特定のプロパティについて、上記のスニペットは、プロパティのコンストラクター関数の参照を__propertyTypes__クラスプロトタイプの非表示プロパティに追加します。例えば:

class Language {
    @JsonMember // String
    name: string;

    @JsonMember// Number
    level: number;
}

class Person {
    @JsonMember // String
    name: string;

    @JsonMember// Language
    language: Language;
}

これで、実行時に必要な型情報が得られ、処理できるようになりました。

 

タイプ情報の処理

最初に使用してObjectインスタンスを取得する必要がありますJSON.parse-その後、__propertyTypes__(上記で収集した)全体を反復処理し、それに応じて必要なプロパティをインスタンス化できます。デシリアライザが開始点を持つように、ルートオブジェクトのタイプを指定する必要があります。

繰り返しますが、このアプローチの完全に単純な実装は次のようになります。

function deserialize<T>(jsonObject: any, Constructor: { new (): T }): T {
    if (!Constructor || !Constructor.prototype.__propertyTypes__ || !jsonObject || typeof jsonObject !== "object") {
        // No root-type with usable type-information is available.
        return jsonObject;
    }

    // Create an instance of root-type.
    var instance: any = new Constructor();

    // For each property marked with @JsonMember, do...
    Object.keys(Constructor.prototype.__propertyTypes__).forEach(propertyKey => {
        var PropertyType = Constructor.prototype.__propertyTypes__[propertyKey];

        // Deserialize recursively, treat property type as root-type.
        instance[propertyKey] = deserialize(jsonObject[propertyKey], PropertyType);
    });

    return instance;
}
var json = '{ "name": "John Doe", "language": { "name": "en", "level": 5 } }';
var person: Person = deserialize(JSON.parse(json), Person);

上記のアイデアには、JSONに存在するものではなく、予想される型(複合/オブジェクト値の場合)によって逆シリアル化するという大きな利点があります。Personが予想される場合、Person作成されるのはインスタンスです。プリミティブ型と配列のための場所でいくつかの追加のセキュリティ対策では、このアプローチは抵抗すること、確実なものとすることができる任意の悪質なJSONを。

 

エッジケース

ただし、解決策がそれほど単純であることに満足している場合は悪いニュースがいくつかあります。対処する必要があるエッジケースが非常に多くあります。そのうちのいくつかだけです:

  • 配列と配列要素(特にネストされた配列)
  • ポリモーフィズム
  • 抽象クラスとインターフェース
  • ...

これらすべてをいじりたくない場合は(たぶんそうしないでください)、このアプローチを使用した概念実証の実用的な実験バージョンであるTypedJSON-私が作成したものをお勧めします。この正確な問題、私が毎日直面している問題に取り組むために。

デコレーターがいまだに実験的であると見なされているため、本番用に使用することはお勧めしませんが、これまでのところうまくいきました。


TypedJSONはうまくいきました。参照していただきありがとうございます。
Neil

すばらしい仕事です。しばらくの間私を悩ませてきた問題に対する非常に洗練された解決策を思い付きました。私はあなたのプロジェクトをとても密接にフォローします!
John Strickler、2017年

12

私はこの男を使って仕事をしてきました:https : //github.com/weichx/cerialize

非常にシンプルでありながら強力です。それはサポートします:

  • オブジェクトのツリー全体のシリアライズとデシリアライズ。
  • 同じオブジェクトの永続的なプロパティと一時的なプロパティ。
  • (逆)シリアル化ロジックをカスタマイズするためのフック。
  • 既存のインスタンス(Angularに最適)に(逆)シリアル化したり、新しいインスタンスを生成したりできます。

例:

class Tree {
  @deserialize public species : string; 
  @deserializeAs(Leaf) public leafs : Array<Leaf>;  //arrays do not need extra specifications, just a type.
  @deserializeAs(Bark, 'barkType') public bark : Bark;  //using custom type and custom key name
  @deserializeIndexable(Leaf) public leafMap : {[idx : string] : Leaf}; //use an object as a map
}

class Leaf {
  @deserialize public color : string;
  @deserialize public blooming : boolean;
  @deserializeAs(Date) public bloomedAt : Date;
}

class Bark {
  @deserialize roughness : number;
}

var json = {
  species: 'Oak',
  barkType: { roughness: 1 },
  leafs: [ {color: 'red', blooming: false, bloomedAt: 'Mon Dec 07 2015 11:48:20 GMT-0500 (EST)' } ],
  leafMap: { type1: { some leaf data }, type2: { some leaf data } }
}
var tree: Tree = Deserialize(json, Tree);

6

TypeScriptインターフェースとランタイムの「タイプマップ」を生成するツールを作成しました。その結果に対してランタイムタイプチェックを実行しますJSON.parsets.quicktype.io

たとえば、次のJSONがあるとします。

{
  "name": "David",
  "pets": [
    {
      "name": "Smoochie",
      "species": "rhino"
    }
  ]
}

quicktypeは、次のTypeScriptインターフェイスと型マップを生成します。

export interface Person {
    name: string;
    pets: Pet[];
}

export interface Pet {
    name:    string;
    species: string;
}

const typeMap: any = {
    Person: {
        name: "string",
        pets: array(object("Pet")),
    },
    Pet: {
        name: "string",
        species: "string",
    },
};

次にJSON.parse、型マップに対して結果を確認します。

export function fromJson(json: string): Person {
    return cast(JSON.parse(json), object("Person"));
}

一部のコードは省略しましたが、詳細についてはquicktypeを試すことができます。


1
何時間もの調査を行い、いくつかの解析手法を試した結果、これは優れたソリューションであると言えます。主に、デコレータがまだ実験的であるためです。*元のリンクが壊れています。しかし、ts.quicktype.ioは機能します。* JSONをJSONスキーマに変換することは、良い最初のステップです。
LexieHankins

3

オプション#5:TypescriptコンストラクターとjQuery.extendの使用

これは最も保守しやすい方法のようです。json構造をパラメーターとして取るコンストラクターを追加し、jsonオブジェクトを拡張します。そうすれば、json構造を解析してアプリケーションモデル全体に​​することができます。

インターフェースを作成したり、コンストラクターでプロパティをリストしたりする必要はありません。

export class Company
{
    Employees : Employee[];

    constructor( jsonData: any )
    {
        jQuery.extend( this, jsonData);

        // apply the same principle to linked objects:
        if ( jsonData.Employees )
            this.Employees = jQuery.map( jsonData.Employees , (emp) => {
                return new Employee ( emp );  });
    }

    calculateSalaries() : void { .... }
}

export class Employee
{
    name: string;
    salary: number;
    city: string;

    constructor( jsonData: any )
    {
        jQuery.extend( this, jsonData);

        // case where your object's property does not match the json's:
        this.city = jsonData.town;
    }
}

給与を計算する会社を受け取るajaxコールバックで:

onReceiveCompany( jsonCompany : any ) 
{
   let newCompany = new Company( jsonCompany );

   // call the methods on your newCompany object ...
   newCompany.calculateSalaries()
}

どこ$.extendから来たの?
whale_steward 2017

@whale_steward著者はjQueryライブラリを参照していると思います。JavaScriptの世界では、「$」はjQueryを使用することが非常に多いです。
Nick Roth

それをインポートする方法?htmlヘッドに含めるだけで十分ですか?
whale_steward 2017

はい、答えを更新して$をjQueryに置き換えます。HTMLヘッドにjQuery.jsをインポートし、package.jsonのdevDependenciesセクションに@ types / jqueryをインストールして追加します。
AnthonyBrenelière2017

1
JavaScriptでは、を実行する必要がありますObject.assign。これにより、jQueryへのこの依存関係が削除されます。
レオンペルティエ2018年

2

単純なオブジェクトの場合、このメソッドが好きです。

class Person {
  constructor(
    public id: String, 
    public name: String, 
    public title: String) {};

  static deserialize(input:any): Person {
    return new Person(input.id, input.name, input.title);
  }
}

var person = Person.deserialize({id: 'P123', name: 'Bob', title: 'Mr'});

コンストラクタでプロパティを定義する機能を活用すると、簡潔になります。

これにより、型指定されたオブジェクト(Object.assignまたはオブジェクトを提供するバリアントを使用するすべての回答)が得られ、外部ライブラリやデコレータは必要ありません。


1

上記の4番目のオプションは、それを行うためのシンプルで優れた方法です。たとえば、サブクラスのオカレンスのいずれかであるメンバーリストなどのクラス階層を処理する必要がある場合は、2番目のオプションと組み合わせる必要があります。メンバースーパークラス。たとえば、DirectorがMemberを拡張する、またはStudentがMemberを拡張する。その場合、サブクラスタイプをjson形式で指定する必要があります


1

jQuery .extendがこれを行います:

var mytsobject = new mytsobject();

var newObj = {a:1,b:2};

$.extend(mytsobject, newObj); //mytsobject will now contain a & b

0

多分実際ではありませんが、簡単な解決策:

interface Bar{
x:number;
y?:string; 
}

var baz:Bar = JSON.parse(jsonString);
alert(baz.y);

難しい依存関係にも対応します!!!


9
このアプローチは実際には期待どおりに機能しません。ランタイム結果を検査bazすると、タイプObjectではなくタイプBar.になりBarますメソッドがないため(プリミティブプロパティのみ)、この単純なケースで機能します。のBarようなメソッドisEnabled()がある場合、そのメソッドはシリアル化されたJSON文字列に含まれないため、このアプローチは失敗します。
トッド

0

ファクトリーを使用する別のオプション

export class A {

    id: number;

    date: Date;

    bId: number;
    readonly b: B;
}

export class B {

    id: number;
}

export class AFactory {

    constructor(
        private readonly createB: BFactory
    ) { }

    create(data: any): A {

        const createB = this.createB.create;

        return Object.assign(new A(),
            data,
            {
                get b(): B {

                    return createB({ id: data.bId });
                },
                date: new Date(data.date)
            });
    }
}

export class BFactory {

    create(data: any): B {

        return Object.assign(new B(), data);
    }
}

https://github.com/MrAntix/ts-deserialize

このように使う

import { A, B, AFactory, BFactory } from "./deserialize";

// create a factory, simplified by DI
const aFactory = new AFactory(new BFactory());

// get an anon js object like you'd get from the http call
const data = { bId: 1, date: '2017-1-1' };

// create a real model from the anon js object
const a = aFactory.create(data);

// confirm instances e.g. dates are Dates 
console.log('a.date is instanceof Date', a.date instanceof Date);
console.log('a.b is instanceof B', a.b instanceof B);
  1. クラスをシンプルに保つ
  2. 柔軟性のために工場で利用可能な注入

0

この目的のために私が見つけた最高のものはクラス変換器です。github.com/typestack/class-transformer

それはあなたがそれを使う方法です:

いくつかのクラス:

export class Foo {

    name: string;

    @Type(() => Bar)
    bar: Bar;

    public someFunction = (test: string): boolean => {
        ...
    }
}


import { plainToClass } from 'class-transformer';

export class SomeService {

  anyFunction() {
u = plainToClass(Foo, JSONobj);
 }

@Typeデコレータを使用すると、ネストされたプロパティも作成されます。


0

個人的には@IngoBürkのオプション#3を好みます。そして、複雑なデータの配列とプリミティブデータの配列をサポートするように彼のコードを改善しました。

interface IDeserializable {
  getTypes(): Object;
}

class Utility {
  static deserializeJson<T>(jsonObj: object, classType: any): T {
    let instanceObj = new classType();
    let types: IDeserializable;
    if (instanceObj && instanceObj.getTypes) {
      types = instanceObj.getTypes();
    }

    for (var prop in jsonObj) {
      if (!(prop in instanceObj)) {
        continue;
      }

      let jsonProp = jsonObj[prop];
      if (this.isObject(jsonProp)) {
        instanceObj[prop] =
          types && types[prop]
            ? this.deserializeJson(jsonProp, types[prop])
            : jsonProp;
      } else if (this.isArray(jsonProp)) {
        instanceObj[prop] = [];
        for (let index = 0; index < jsonProp.length; index++) {
          const elem = jsonProp[index];
          if (this.isObject(elem) && types && types[prop]) {
            instanceObj[prop].push(this.deserializeJson(elem, types[prop]));
          } else {
            instanceObj[prop].push(elem);
          }
        }
      } else {
        instanceObj[prop] = jsonProp;
      }
    }

    return instanceObj;
  }

  //#region ### get types ###
  /**
   * check type of value be string
   * @param {*} value
   */
  static isString(value: any) {
    return typeof value === "string" || value instanceof String;
  }

  /**
   * check type of value be array
   * @param {*} value
   */
  static isNumber(value: any) {
    return typeof value === "number" && isFinite(value);
  }

  /**
   * check type of value be array
   * @param {*} value
   */
  static isArray(value: any) {
    return value && typeof value === "object" && value.constructor === Array;
  }

  /**
   * check type of value be object
   * @param {*} value
   */
  static isObject(value: any) {
    return value && typeof value === "object" && value.constructor === Object;
  }

  /**
   * check type of value be boolean
   * @param {*} value
   */
  static isBoolean(value: any) {
    return typeof value === "boolean";
  }
  //#endregion
}

// #region ### Models ###
class Hotel implements IDeserializable {
  id: number = 0;
  name: string = "";
  address: string = "";
  city: City = new City(); // complex data
  roomTypes: Array<RoomType> = []; // array of complex data
  facilities: Array<string> = []; // array of primitive data

  // getter example
  get nameAndAddress() {
    return `${this.name} ${this.address}`;
  }

  // function example
  checkRoom() {
    return true;
  }

  // this function will be use for getting run-time type information
  getTypes() {
    return {
      city: City,
      roomTypes: RoomType
    };
  }
}

class RoomType implements IDeserializable {
  id: number = 0;
  name: string = "";
  roomPrices: Array<RoomPrice> = [];

  // getter example
  get totalPrice() {
    return this.roomPrices.map(x => x.price).reduce((a, b) => a + b, 0);
  }

  getTypes() {
    return {
      roomPrices: RoomPrice
    };
  }
}

class RoomPrice {
  price: number = 0;
  date: string = "";
}

class City {
  id: number = 0;
  name: string = "";
}
// #endregion

// #region ### test code ###
var jsonObj = {
  id: 1,
  name: "hotel1",
  address: "address1",
  city: {
    id: 1,
    name: "city1"
  },
  roomTypes: [
    {
      id: 1,
      name: "single",
      roomPrices: [
        {
          price: 1000,
          date: "2020-02-20"
        },
        {
          price: 1500,
          date: "2020-02-21"
        }
      ]
    },
    {
      id: 2,
      name: "double",
      roomPrices: [
        {
          price: 2000,
          date: "2020-02-20"
        },
        {
          price: 2500,
          date: "2020-02-21"
        }
      ]
    }
  ],
  facilities: ["facility1", "facility2"]
};

var hotelInstance = Utility.deserializeJson<Hotel>(jsonObj, Hotel);

console.log(hotelInstance.city.name);
console.log(hotelInstance.nameAndAddress); // getter
console.log(hotelInstance.checkRoom()); // function
console.log(hotelInstance.roomTypes[0].totalPrice); // getter
// #endregion

-1

あなたは以下のようにすることができます

export interface Instance {
  id?:string;
  name?:string;
  type:string;
}

そして

var instance: Instance = <Instance>({
      id: null,
      name: '',
      type: ''
    });

これは、実際に期待されるオブジェクトタイプのランタイムインスタンスを生成しません。タイプにプリミティブプロパティしかない場合は機能するように見えますが、タイプにメソッドがある場合は失敗します。インターフェース定義も実行時には使用できません(ビルド時のみ)。
トッド、

-1
**model.ts**
export class Item {
    private key: JSON;
    constructor(jsonItem: any) {
        this.key = jsonItem;
    }
}

**service.ts**
import { Item } from '../model/items';

export class ItemService {
    items: Item;
    constructor() {
        this.items = new Item({
            'logo': 'Logo',
            'home': 'Home',
            'about': 'About',
            'contact': 'Contact',
        });
    }
    getItems(): Item {
        return this.items;
    }
}

例:以下のようなコンテンツを呼び出す
user8390810

<a class="navbar-brand" href="#"> {{keyItems.key.logo}} </a>
user8390810 '27

これは「必要に応じてクラスをインスタンス化する」ようには見えません。
LexieHankins
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.