Jak zainicjować obiekt TypeScript za pomocą JSON-Object?

Otrzymuję obiekt JSON z połączenia AJAX na serwer REST. Ten obiekt ma nazwy właściwości, które pasują do mojej klasy maszynopisu(jest to kontynuacja tego pytania ).

Jaki jest najlepszy sposób inicjalizacji? Nie sądzę, aby to zadziałało, ponieważ Klasa (obiekt& JSON) ma członków, które są listami obiektów i członków, które są klasami, a te klasy mają członków, które są listami i / lub klasami.

Ale wolałbym podejście, które wyszukuje nazwy członków i przypisuje je, tworząc listy i tworząc instancje klas w razie potrzeby ,więc nie muszę pisać jawnego kodu dla każdego członka w każdej klasie (jest ich dużo!)

Author: leonheess, 2014-04-05

17 answers

Oto kilka szybkich ujęć, aby pokazać kilka różnych sposobów. Nie są one bynajmniej "kompletne" i jako zastrzeżenie, nie sądzę, że to dobry pomysł, aby to zrobić w ten sposób. Również kod nie jest zbyt czysty, ponieważ po prostu wpisałem go razem dość szybko.

Również jako Uwaga: oczywiście klasy deserializable muszą mieć domyślne konstruktory, jak to ma miejsce we wszystkich innych językach, w których jestem świadomy deserializacji dowolnego rodzaju. Oczywiście, Javascript nie będzie narzekać, jeśli wywołasz non-default konstruktor bez argumentów, ale klasa powinna być do tego przygotowana (poza tym nie byłaby to tak naprawdę "typescripty").

Opcja #1: Brak informacji o czasie wykonywania

Problem z tym podejściem polega głównie na tym, że nazwa każdego członka musi pasować do jego klasy. Co automatycznie ogranicza cię do jednego członka tego samego typu na klasę i łamie kilka zasad dobrej praktyki. Zdecydowanie odradzam, ale wymień to tutaj, bo to był pierwszy "szkic" kiedy pisałem tej odpowiedzi (dlatego też nazwy są " Foo " itp.).

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);

Opcja # 2: Nazwa właściwość

Aby pozbyć się problemu w opcji # 1, musimy mieć jakieś informacje o tym, jakiego typu jest węzeł w obiekcie JSON. Problem polega na tym, że w Typescripcie są to konstrukcje kompilowane i potrzebujemy ich w trybie runtime-ale obiekty runtime po prostu nie mają świadomości swoich właściwości, dopóki nie zostaną ustawione.

Jednym ze sposobów jest uświadomienie klasom ich imion. Potrzebujesz też tej nieruchomości w JSON. W rzeczywistości, ty tylko potrzebujesz go w 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);

Opcja # 3: jawne określanie typów członków

Jak wspomniano powyżej, informacje o typie członków klasy nie są dostępne w czasie wykonywania – czyli chyba, że je udostępnimy. Musimy to zrobić tylko dla nie-prymitywnych członków i możemy iść: {]}

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);

Opcja # 4: wyrazisty, ale schludny sposób

Aktualizacja 01/03/2016: Jako @ GameAlchemist wskazał w komentarzach ([[31]] pomysł, implementacja ), począwszy od Typescript 1.7, rozwiązanie opisane poniżej może być napisane w lepszy sposób przy użyciu dekoratorów klas/właściwości.

Serializacja jest zawsze problemem i moim zdaniem najlepszym sposobem jest sposób, który po prostu nie jest najkrótszy. Ze wszystkich opcji, to jest to, co wolałbym, ponieważ autor klasy ma pełną kontrolę nad stanem deserializowanych obiektów. Gdybym miał zgadywać, powiedziałbym, że wszystkie inne opcje, prędzej czy później, sprawią ci kłopoty(chyba że Javascript wymyśli natywny sposób radzenia sobie z tym).

Naprawdę, poniższy przykład nie oddaje sprawiedliwości elastyczności. To naprawdę kopiuje strukturę klasy. Różnica, o której musisz pamiętać, jest taka, że klasa ma pełną kontrolę nad używaniem dowolnego JSON, który chce kontrolować stan całej klasy (możesz obliczać rzeczy itp.).

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);
 195
Author: Ingo Bürk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-14 15:31:47

Możesz użyć Object.assign Nie wiem kiedy to zostało dodane, obecnie używam Typescript 2.0.2, a to wygląda na funkcję ES6.

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

Oto HalJson

export class HalJson {
    _links: HalLinks;
}

export class HalLinks implements Links {
}

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

export interface Link {
    readonly href: URL;
}

Oto co mówi chrome

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

Więc widać, że nie robi przypisywania rekurencyjnie

 43
Author: xenoterracide,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-05 02:54:22

TLDR: TypedJSON (working proof of concept)


Źródłem złożoności tego problemu jest to, że musimy deserializować JSON w runtime używając informacji typu, które istnieją tylko w podczas kompilacji. Wymaga to, aby informacje o typie były w jakiś sposób dostępne w czasie wykonywania.

Na szczęście można to rozwiązać w bardzo elegancki i solidny sposób za pomocą dekoratorów i ReflectDecorators :

  1. użyj dekoratorów właściwości na właściwościach, które podlegają serializacji, aby zapisać informacje o metadanych i przechowywać te informacje gdzieś, na przykład na prototypie klasy
  2. W ten sposób można uzyskać dostęp do informacji o metadanych.]}

 

Typ Zapisu-Informacja

Z kombinacją ReflectDecorators I property decorators, informacje typu mogą być łatwo zapisane o nieruchomości. Podstawową implementacją tego podejścia byłoby:

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);
}

Dla danej właściwości, powyższy fragment doda odniesienie do funkcji konstruktora właściwości do ukrytej właściwości __propertyTypes__ w prototypie klasy. Na przykład:

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

    @JsonMember// Number
    level: number;
}

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

    @JsonMember// Language
    language: Language;
}

I to wszystko, mamy wymagane informacje typu w czasie wykonywania, które mogą być teraz przetwarzane.

 

Typ Przetwarzania-Informacja

Najpierw musimy uzyskać Object instancja za pomocą JSON.parse -- Następnie możemy iterację nad całkami w __propertyTypes__ (zebranymi powyżej) i odpowiednio utworzyć instancję wymaganych właściwości. Należy określić typ obiektu głównego, aby deserializer miał punkt początkowy.

[[10]} ponownie, martwą prostą implementacją tego podejścia byłoby:
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);

Powyższa idea ma dużą zaletę deserializacji przez oczekiwane typy (dla wartości złożonych/obiektowych), zamiast tego, co jest obecne w JSON. Jeśli oczekuje się Person, to jest to instancja Person, która zostanie utworzona. Z pewnymi dodatkowymi środkami bezpieczeństwa dla prymitywnych typów i tablic, takie podejście może być zabezpieczone, że odporne any złośliwy JSON.

 

Edge Cases

Jednakże, jeśli jesteś teraz zadowolony, że rozwiązanie jest że proste, mam złe wieści: istnieje Ogromna liczba przypadków krawędzi, które należy zająć. Tylko niektóre z nich są:

  • tablice i elementy tablicy (szczególnie w tablicach zagnieżdżonych)
  • polimorfizm
  • klasy abstrakcyjne i interfejsy
  • ...

Jeśli nie chcesz bawić się tymi wszystkimi (założę się, że nie), z przyjemnością polecam działającą eksperymentalną wersję proof-of-concept wykorzystującą to podejście, TypedJSON -- który stworzyłem, aby rozwiązać ten dokładny problem, problem, z którym stykam się codziennie.

Ze względu na to, jak dekoratorzy są nadal uważane za eksperymentalne, nie polecam używania go do użytku produkcyjnego, ale do tej pory dobrze mi służył.

 36
Author: John Weisz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-02 13:09:46

Wykorzystałem tego gościa do pracy: https://github.com/weichx/cerialize

To bardzo proste, ale potężne. Obsługuje:
  • serializacja i deserializacja całego drzewa obiektów.
  • trwałe i przejściowe właściwości tego samego obiektu.
  • Hooki do dostosowania logiki serializacji (de).
  • może (de)serializować do istniejącej instancji (świetne dla Angular) lub generować nowe przypadki.
  • itd.

Przykład:

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);
 12
Author: André,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-18 13:07:27

Stworzyłem narzędzie, które generuje Interfejsy TypeScript i runtime "type map" do wykonywania Runtime typechecking na podstawie wyników JSON.parse: ts.quicktype.io

Na przykład, biorąc pod uwagę ten JSON:

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

Quicktype tworzy następujący interfejs TypeScript i mapę typów:

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",
    },
};

Następnie sprawdzamy wynik JSON.parse na mapie typu:

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

Pominąłem trochę kodu, ale możesz spróbować quicktype po szczegóły.

 10
Author: David Siegel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-16 19:03:26

Dla prostych obiektów, podoba mi się ta metoda:

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'});

Wykorzystanie możliwości definiowania właściwości w konstruktorze pozwala na zwięzłość.

To daje typowany obiekt (w porównaniu do wszystkich odpowiedzi, które używają obiektu.przypisać lub jakiś wariant, który daje obiekt) i nie wymaga zewnętrznych bibliotek ani dekoratorów.

 5
Author: stevex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-28 19:52:09

Opcja # 5: używanie konstruktorów maszynopisu i jQuery.extend

To wydaje się być najbardziej utrzymywalną metodą: dodać konstruktor, który przyjmuje jako parametr strukturę json i rozszerzyć obiekt json. W ten sposób można przetworzyć strukturę json do całego modelu aplikacji.

Nie ma potrzeby tworzenia interfejsów ani listowania właściwości w konstruktorze.

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;
    }
}

W Twoim wywołaniu zwrotnym ajax, w którym otrzymujesz firmę do obliczania wynagrodzeń:

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

   // call the methods on your newCompany object ...
   newCompany.calculateSalaries()
}
 2
Author: Anthony Brenelière,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-06 08:47:11

4. opcja opisana powyżej jest prostym i przyjemnym sposobem, aby to zrobić, który musi być połączony z 2. opcją w przypadku, gdy musisz obsługiwać hierarchię klas, jak na przykład lista członków, która jest którymkolwiek z wystąpień podklas członka Super klasy, np. Director extends Member lub Student extends Member. W takim przypadku musisz podać typ podklasy w formacie json

 1
Author: Xavier Méhaut,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-02 03:29:52

Może nie rzeczywiste, ale proste rozwiązanie:

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

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

Pracuj także nad trudnymi zależnościami!!!

 1
Author: Mykhailo Pylyp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-28 09:30:14

JQuery .extend robi to za ciebie:

var mytsobject = new mytsobject();

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

$.extend(mytsobject, newObj); //mytsobject will now contain a & b
 1
Author: Daniel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-12 14:55:10

Najlepszy jaki znalazłem do tego celu to class-transformer. github.com/typestack/class-transformer

Tak się go używa:

Jakaś klasa:

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);
 }

Jeśli użyjesz dekoratora @ Type, również zostaną utworzone zagnieżdżone właściwości.

 1
Author: Fabianus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-02-26 20:07:13

Inna opcja przy użyciu fabryk

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

Użyj TAK

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. sprawia, że Twoje zajęcia są proste
  2. Wtrysk Dostępny dla fabryk dla elastyczności
 0
Author: Anthony Johnston,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-13 21:50:50

Ja osobiście wolę opcję # 3 z @Ingo Bürk. I ulepszyłem jego kody, aby obsługiwały szereg złożonych danych i szereg prymitywnych danych.

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

 0
Author: alireza etemadi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-26 09:50:20

Moje podejście jest nieco inne. Nie kopiuję właściwości do nowych instancji, po prostu zmieniam prototyp istniejących Pojo (może nie działać dobrze na starszych przeglądarkach). Każda klasa jest odpowiedzialna za dostarczenie metody Setprototyps do ustawiania prototoypów dowolnych obiektów potomnych, które z kolei dostarczają własne metody Setprototyps.

(używam również właściwości _Type, aby uzyskać nazwę klasy nieznanych obiektów, ale można ją tutaj zignorować)

class ParentClass
{
    public ID?: Guid;
    public Child?: ChildClass;
    public ListOfChildren?: ChildClass[];

    /**
     * Set the prototypes of all objects in the graph.
     * Used for recursive prototype assignment on a graph via ObjectUtils.SetPrototypeOf.
     * @param pojo Plain object received from API/JSON to be given the class prototype.
     */
    private static SetPrototypes(pojo: ParentClass): void
    {
        ObjectUtils.SetPrototypeOf(pojo.Child, ChildClass);
        ObjectUtils.SetPrototypeOfAll(pojo.ListOfChildren, ChildClass);
    }
}

class ChildClass
{
    public ID?: Guid;
    public GrandChild?: GrandChildClass;

    /**
     * Set the prototypes of all objects in the graph.
     * Used for recursive prototype assignment on a graph via ObjectUtils.SetPrototypeOf.
     * @param pojo Plain object received from API/JSON to be given the class prototype.
     */
    private static SetPrototypes(pojo: ChildClass): void
    {
        ObjectUtils.SetPrototypeOf(pojo.GrandChild, GrandChildClass);
    }
}

Oto Przedmioty.ts:

/**
 * ClassType lets us specify arguments as class variables.
 * (where ClassType == window[ClassName])
 */
type ClassType = { new(...args: any[]): any; };

/**
 * The name of a class as opposed to the class itself.
 * (where ClassType == window[ClassName])
 */
type ClassName = string & {};

abstract class ObjectUtils
{
/**
 * Set the prototype of an object to the specified class.
 *
 * Does nothing if source or type are null.
 * Throws an exception if type is not a known class type.
 *
 * If type has the SetPrototypes method then that is called on the source
 * to perform recursive prototype assignment on an object graph.
 *
 * SetPrototypes is declared private on types because it should only be called
 * by this method. It does not (and must not) set the prototype of the object
 * itself - only the protoypes of child properties, otherwise it would cause a
 * loop. Thus a public method would be misleading and not useful on its own.
 * 
 * https://stackoverflow.com/questions/9959727/proto-vs-prototype-in-javascript
 */
public static SetPrototypeOf(source: any, type: ClassType | ClassName): any
{
    let classType = (typeof type === "string") ? window[type] : type;

    if (!source || !classType)
    {
        return source;
    }

    // Guard/contract utility
    ExGuard.IsValid(classType.prototype, "type", <any>type);

    if ((<any>Object).setPrototypeOf)
    {
        (<any>Object).setPrototypeOf(source, classType.prototype);
    }
    else if (source.__proto__)
    {
        source.__proto__ = classType.prototype.__proto__;
    }

    if (typeof classType["SetPrototypes"] === "function")
    {
        classType["SetPrototypes"](source);
    }

    return source;
}

/**
 * Set the prototype of a list of objects to the specified class.
 * 
 * Throws an exception if type is not a known class type.
 */
public static SetPrototypeOfAll(source: any[], type: ClassType): void
{
    if (!source)
    {
        return;
    }

    for (var i = 0; i < source.length; i++)
    {
        this.SetPrototypeOf(source[i], type);
    }
}
}

Użycie:

let pojo = SomePlainOldJavascriptObjectReceivedViaAjax;

let parentObject = ObjectUtils.SetPrototypeOf(pojo, ParentClass);

// parentObject is now a proper ParentClass instance
 0
Author: Etherman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-20 15:07:59

Oto moje podejście (bardzo proste):

const jsonObj: { [key: string]: any } = JSON.parse(jsonStr);

for (const key in jsonObj) {
  if (!jsonObj.hasOwnProperty(key)) {
    continue;
  }

  console.log(key); // Key
  console.log(jsonObj[key]); // Value
  // Your logic...
}
 0
Author: KimchiMan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-03 06:50:20

Możesz zrobić jak poniżej

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

I

var instance: Instance = <Instance>({
      id: null,
      name: '',
      type: ''
    });
 -1
Author: Md Ayub Ali Sarker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-19 16:10:04
**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;
    }
}
 -1
Author: user8390810,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-27 04:15:43