TypeScript nesnesini JSON nesnesiyle nasıl başlatırım


199

Bir AJAX çağrısından bir REST sunucusuna bir JSON nesnesi alıyorum. Bu nesnenin TypeScript sınıfımla eşleşen özellik adları var (bu, bu sorunun bir devamıdır ).

İlk kullanıma hazırlamanın en iyi yolu nedir? Sınıf (& JSON nesnesi) nesneleri ve sınıfları olan üyelerin üyeleri ve bu sınıflar listeleri ve / veya sınıfları olan üyeleri var çünkü bu işe yaramaz sanmıyorum .

Ama üye adlarını arar ve onları atayarak, listeler oluştururken ve gerektiğinde sınıfları başlatan bir yaklaşımı tercih ederim, bu yüzden her sınıftaki her üye için açık kod yazmak zorunda değilim (çok fazla!)


1
Neden tekrar sordunuz (diğer soruda verdiğim cevap bunun işe yaramayacağını ve özelliklerin mevcut bir nesneye kopyalanmasıyla ilgili olduğunu söyledi)?
WiredPrairie


3
@WiredPrairie bu soru farklı, özellikleri teker teker yürüyüp atayabileceğimi soruyor. Diğer sorular ise bunu yapıp yapamayacağımı soruyordu.
David Thielen

1
@WiredPrairie cont: Sadece ilkel türlere ulaşana kadar özelliklere dalmaya devam ederseniz, bunlar arasında atanabilir.
David Thielen

2
Yine de yapmanız gereken önerdiğim gibi tüm değerleri kopyalıyor. JavaScript'in temel tasarımı olduğu için bunu TypeScript'te yapmanın yeni bir yolu yoktur. Büyük nesneler için herhangi bir değeri kopyalamak ve bunun yerine veri yapısına "işlem yapmak" isteyebilirsiniz.
WiredPrairie

Yanıtlar:


189

Bunlar, birkaç farklı yol göstermek için bazı hızlı çekimlerdir. Onlar hiçbir şekilde "tam" değildir ve bir feragatname olarak, bunu böyle yapmanın iyi bir fikir olduğunu düşünmüyorum. Ayrıca sadece çok hızlı bir şekilde birlikte yazdım çünkü kod çok temiz değil.

Ayrıca not olarak: Tabii ki serileştirilebilir sınıfların, herhangi bir türün serileştirilmesinin farkında olduğum diğer tüm dillerde olduğu gibi varsayılan kuruculara sahip olması gerekir. Elbette, argüman içermeyen varsayılan olmayan bir kurucu çağırırsanız Javascript şikayet etmez, ancak sınıf bunun için daha iyi hazırlanır (artı, gerçekten "daktilo yolu" olmaz).

Seçenek # 1: Hiç çalışma zamanı bilgisi yok

Bu yaklaşımdaki sorun çoğunlukla herhangi bir üyenin adının sınıfıyla eşleşmesi gerektiğidir. Bu, sizi otomatik olarak sınıf başına aynı türden bir üyeyle sınırlar ve birkaç iyi uygulama kuralını ihlal eder. Buna şiddetle tavsiye ediyorum, ama sadece burada listeliyorum, çünkü bu cevabı yazdığımda ilk "taslak" idi (bu yüzden isimler "Foo" vb.).

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

Seçenek # 2: name özelliği

Seçenek # 1'deki sorundan kurtulmak için, JSON nesnesindeki bir düğümün türü hakkında bir tür bilgiye ihtiyacımız var. Sorun, Typcript'te, bu şeylerin derleme zamanı yapıları olması ve çalışma zamanında onlara ihtiyaç duymamızdır - ancak çalışma zamanı nesneleri, ayarlanana kadar özelliklerinin farkında değildir.

Bunu yapmanın bir yolu sınıfları isimlerinden haberdar etmektir. Yine de JSON bu özelliği gerekir. Aslında, sadece json'da ihtiyacınız var:

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

Seçenek # 3: Üye türlerini açıkça belirtme

Yukarıda belirtildiği gibi, sınıf üyelerinin tür bilgisi çalışma zamanında mevcut değildir - yani biz bunu mümkün kılmadıkça. Bunu sadece ilkel olmayan üyeler için yapmamız gerekiyor ve gitmeye hazırız:

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

Seçenek # 4: Ayrıntılı, ama düzgün yol

Güncelleme 01/03/2016: @GameAlchemist'in yorumlarda belirttiği gibi ( fikir , uygulama ) 1.7'den itibaren, aşağıda açıklanan çözüm sınıf / özellik dekoratörleri kullanılarak daha iyi bir şekilde yazılabilir.

Serileştirme her zaman bir sorundur ve bence en iyi yol en kısa yol değildir. Tüm seçenekler arasından tercih ettiğim şey budur çünkü sınıfın yazarı, serileştirilmiş nesnelerin durumu üzerinde tam kontrole sahiptir. Tahmin etmek zorunda kalsaydım, er ya da geç diğer tüm seçeneklerin başınızı belaya sokacağını söyleyebilirim (Javascript bununla başa çıkmak için yerel bir yol bulmazsa).

Gerçekten, aşağıdaki örnek esneklik adaletini yapmaz. Gerçekten sadece sınıfın yapısını kopyalar. Burada aklınızda bulundurmanız gereken fark, sınıfın tüm sınıfın durumunu kontrol etmek istediği herhangi bir JSON'u kullanmak için tam kontrole sahip olmasıdır (bir şeyler hesaplayabilirsiniz vb.).

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
Seçenek # 4, gitmek için makul bir yol diyorum. Yine de serileştirme kodunu yazmanız gerekiyor, ancak aynı sınıfta ve tamamen kontrol edilebilir. Java'dan geliyorsanız, bu yazma equalsveya toStringyöntemlerle karşılaştırılabilir (yalnızca bunları otomatik olarak oluşturduğunuz için). İsterseniz bir jeneratör yazmak çok zor olmamalı deserialize, ancak çalışma zamanı otomasyonu olamaz.
Ingo Bürk

2
@ IngoBürk, bu soruyu 2 yıl sonra sorduğumu biliyorum ama bu nesne dizisinde nasıl çalışacak? Yukarıdaki örnek kod JSON nesnesi için iyi çalışıyor. nesne dizisi için nasıl kullanılabilir?
Pratik Gaikwad

2
Bir yan açıklama: 1.7'den beri (kuşkusuz cevabınızdan daha yeni), typcript, 4. çözümü daha temiz bir şekilde yazmanıza izin veren sınıf / özellik dekoratörleri sağlar.
GameAlchemist

1
Bulduğum en iyi belgeler bir StackOverflow cevabı: stackoverflow.com/a/29837695/856501 . Bir projede dekoratörler kullandım ve birkaç özellik daha istememe rağmen bir cazibe gibi çalıştıklarını söylemeliyim.
GameAlchemist

2
Henüz bir üretim projesi için dekoratörlere atlamam - hala deneysel bir özellik olduğunu unutmayın. Gerçek dünya kodunu "deneyler" üzerine dayandırmazdım, çünkü endişelendiğimiz sürece bir sonraki versiyonda gitmiş olabilirler ve bir sürü kodu yeniden yazmanız veya sonsuza kadar eski bir TS versiyonuna takılı kalmanız gerekir. Sadece benim $ .02
RVP

35

kullanabileceğiniz Object.assignbu, Şu anda typescript 2.0.2 kullanıyorum eklendiğinde bilmiyorum ve bu görünür bir ES6 özelliği olması.

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

burada HalJson

export class HalJson {
    _links: HalLinks;
}

export class HalLinks implements Links {
}

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

export interface Link {
    readonly href: URL;
}

krom böyle söylüyor

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

Böylece atamayı tekrar tekrar yapmadığını görebilirsiniz


2
böylece, temelde, o da şu: Object.assign. Neden bunun üzerinde iki sözlük benzeri cevabımız var?
phil294

18
@Blauhim Çünkü Object.assignözyinelemeli olarak çalışmaz ve değerleri Objectörnek olarak bırakarak doğru nesne türlerini somutlaştırmaz . Önemsiz görevler için iyi olsa da, karmaşık tip serileştirme mümkün değildir. Örneğin, bir sınıf özelliği özel bir sınıf türündeyse , JSON.parse+ Object.assignbu özelliği başlatacaktır Object. Yan etkiler eksik yöntemler ve erişimcileri içerir.
John Weisz

@JohnWeisz üst düzey nesne atama sınıfı doğru tipe sahip ve ben bu konuda özyinelemeden bahsettim ... ki, YMMV ve bunlar anlaşma kırıcılar olabilir.
Ocak'ta xenoterracide

Doğrudan sorudan alıntı: "sınıf nesneleri ve sınıfları olan üyelerin listesi vardır ve bu sınıflar listeleri ve / veya sınıfları olan üyeleri var [...] Ben üye bakan bir yaklaşım tercih ederim isimleri ve onları atar, listeleri oluşturma ve gerektiği gibi sınıfları örnekleme, bu yüzden her sınıftaki her üye için açık kod yazmak zorunda değilim " - durum böyle değil Object.assign, nerede hala iç içe örnekleme yazmaya geliyor el. Bu yaklaşım çok basit, öğretici düzeyindeki nesneler için iyidir, ancak gerçek kullanım için uygun değildir.
John Weisz

@JohnWeisz, çoğunlukla bununla cevap verdi, çünkü herhangi bir cevapta değildi ve bazı kullanım durumları için basit görünüyordu. Aradığınızı yapmak için yansıma gibi diğer cevaplarla birlikte de kullanılabileceğinden eminim. Bunu daha sonra hatırlayabilmek için kısmen yazdım. Bu cevaplara baktığımızda ve çok daha güçlü kütüphaneler kullandığınızda ve yazdığınızda "gerçek kullanım" için uygun hiçbir şey görünmüyor.
Ocak'ta xenoterracide

34

TLDR: TypedJSON (kavramın çalışma kanıtı)


Bu sorunun karmaşıklığının kökü, yalnızca derleme zamanında bulunan tür bilgilerini kullanarak çalışma zamanında JSON'un serisini kaldırmamız gerektiğidir . Bu, tür bilgilerinin çalışma zamanında bir şekilde kullanılabilir olmasını gerektirir.

Neyse ki, bu dekoratörler ve ReflectDecorators ile çok zarif ve sağlam bir şekilde çözülebilir :

  1. Serileştirmeye tabi olan özellikler üzerinde, meta veri bilgilerini kaydetmek ve bu bilgileri örneğin sınıf prototipinde bir yerde saklamak için özellik dekoratörleri kullanın
  2. Bu meta veri bilgilerini özyinelemeli başlatıcıya (serileştirici) aktarın

 

Kayıt Türü Bilgileri

ReflectDecorators ve özellik dekoratörlerinin bir kombinasyonu ile tür bilgisi bir özellik hakkında kolayca kaydedilebilir. Bu yaklaşımın ilkel bir uygulaması şu şekildedir:

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

Belirli bir özellik için, yukarıdaki snippet __propertyTypes__, sınıf prototipindeki gizli özelliğe özelliğin yapıcı işlevinin bir başvurusunu ekler . Örneğin:

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

    @JsonMember// Number
    level: number;
}

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

    @JsonMember// Language
    language: Language;
}

Ve işte bu, çalışma zamanında gerekli işlenebilen tip bilgisine sahibiz.

 

İşleme Türü Bilgileri

Öncelikle Objectkullanarak bir örnek elde JSON.parseetmeliyiz - bundan sonra, __propertyTypes__(yukarıda toplanan) girişleri tekrarlayabilir ve gerekli özellikleri buna göre başlatabiliriz. Deserializer'ın bir başlangıç ​​noktasına sahip olması için kök nesnenin türü belirtilmelidir.

Yine, bu yaklaşımın ölü basit bir uygulaması şöyle olacaktır:

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

Yukarıdaki fikrin, JSON'da mevcut olanlar yerine, beklenen türlere göre (karmaşık / nesne değerleri için) serileştirmenin büyük bir avantajı vardır . A Personbekleniyorsa, Personoluşturulan bir örnektir. İlkel türler ve diziler için bazı ek güvenlik önlemleri uygulandığında, bu yaklaşım güvenli hale getirilebilir, bu da tüm kötü amaçlı JSON'lara direnir .

 

Kenar Kılıfları

Ancak, çözümün bu kadar basit olmasından memnunsanız, bazı kötü haberlerim var: ilgilenilmesi gereken çok sayıda kenar vaka var. Sadece bazıları:

  • Diziler ve dizi elemanları (özellikle iç içe dizilerde)
  • Polimorfizm
  • Soyut sınıflar ve arayüzler
  • ...

Tüm bunlarla uğraşmak istemiyorsanız (bahse girmediğinizden emin değilim), bu yaklaşımı kullanan bir kavram kanıtı olan TypedJSON'un çalışan deneysel bir versiyonunu tavsiye etmekten memnuniyet duyarım. bu kesin problemle başa çıkmak için, günlük olarak kendimle yüzleştiğim bir problem.

Dekoratörlerin hala nasıl deneysel olduğu düşünüldüğünden, üretim kullanımı için kullanmanızı tavsiye etmem, ancak şu ana kadar bana iyi hizmet etti.


TypedJSON harika çalıştı; referans için çok teşekkürler.
Neil

Harika iş, bir süredir beni rahatsız eden bir soruna çok zarif bir çözüm buldunuz. Projenizi çok yakından takip edeceğim!
John Strickler

12

Bu adamı işi yapmak için kullanıyorum: https://github.com/weichx/cerialize

Çok basit ama güçlü. Destekler:

  • Bütün bir nesne ağacının serileştirilmesi ve serileştirilmesi.
  • Aynı nesne üzerinde kalıcı ve geçici özellikler.
  • Serileştirme mantığını (de) özelleştirmek için kancalar.
  • Mevcut bir örneğe (Açısal için harika) serileştirebilir veya yeni örnekler oluşturabilir.
  • vb.

Misal:

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

Ben typescript arayüzleri ve sonuçlarına karşı çalışma zamanı typechecking gerçekleştirmek için bir çalışma zamanı "tipi haritası" üreten bir araç oluşturduk JSON.parse: ts.quicktype.io

Örneğin, bu JSON verildiğinde:

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

quicktype aşağıdaki TypeScript arabirimini ve tür eşlemesini üretir:

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

Sonra JSON.parsetür haritasının sonucunu kontrol ediyoruz :

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

Bazı kodunu bıraktım ama deneyebileceğiniz QuickType detayları için.


1
Saatlerce araştırma yaptıktan ve birkaç ayrıştırma tekniğinde elimi denedikten sonra, bunun mükemmel bir çözüm olduğunu söyleyebilirim - esas olarak dekoratörler hala deneysel. * Orijinal bağlantı benim için bozuk; ancak ts.quicktype.io çalışır. * JSON'u JSON Schema'ya dönüştürmek iyi bir ilk adımdır.
LexieHankins

3

Seçenek # 5: Typescript yapıcılarını ve jQuery.extend kullanma

Bu en sürdürülebilir yöntem gibi görünüyor: json yapısını parametre olarak alan bir yapıcı ekleyin ve json nesnesini genişletin. Bu şekilde, bir json yapısını tüm uygulama modeline ayrıştırabilirsiniz.

Arabirim oluşturmaya veya yapıcıda özellikleri listelemeye gerek yoktur.

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 geri çağrınızda maaşları hesaplamak için bir şirket alırsınız:

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

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

nereden $.extendgeliyor?
whale_steward

@whale_steward Yazarın jQuery kütüphanesine atıfta bulunduğunu varsayarım. JavaScript dünyasında '$' genellikle jQuery kullanan birisidir.
Nick Roth

nasıl alınır? sadece html kafasına dahil etmek yeterlidir?
whale_steward

evet jQuery tarafından $ yerine cevap güncelleyin. html başlığına jQuery.js dosyasını içe aktarın ve package.json, devDependencies bölümüne @ types / jquery yükleyip ekleyin.
Anthony Brenelière

1
Javascript, yapmanız gerekir Object.assign, bu jQuery bağımlılığını kaldırır.
Léon Pelletier

2

Basit nesneler için bu yöntemi seviyorum:

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

Yapıcıdaki özellikleri tanımlama yeteneğinden yararlanmak, uygulamanın özlü olmasını sağlar.

Bu size yazılan bir nesneyi (Object.assign veya size bir Object veren bazı varyantları kullanan tüm yanıtlara karşı) getirir ve harici kitaplıklar veya dekoratörler gerektirmez.


1

Yukarıda açıklanan 4. seçenek, bunu yapmak için basit ve güzel bir yoldur; örneğin, bir sınıf hiyerarşisini, örneğin Üye süper sınıfı, örneğin, Yönetici Üyeyi genişletir veya Öğrenci Üyeyi genişletir. Bu durumda alt sınıf türünü json biçiminde vermeniz gerekir


1

JQuery .extend bunu sizin için yapar:

var mytsobject = new mytsobject();

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

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

1

bu amaçla bulduğum en iyi şey sınıf transformatörü. github.com/typestack/class-transformer

Bu şekilde kullanıyorsunuz:

Bazı sınıflar:

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 dekoratör kullanırsanız, yuvalanmış özellikleri de oluşturulur.


0

Belki gerçek değil, basit bir çözüm:

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

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

zor bağımlılıklar için de çalışmak !!!


9
Bu yaklaşım aslında beklendiği gibi çalışmaz. Çalışma zamanı sonuçlarını incelerseniz, baztür olacak ve tür Objectolmayacak Bar.Bu basit durumda çalışır çünkü Baryöntemi yoktur (sadece ilkel özellikler). Eğer Barbir yöntem gibi oldu isEnabled()bu yöntem tefrika JSON dizesinde olmaz çünkü, bu yaklaşım başarısız olur.
Todd

0

Fabrikaları kullanan başka bir seçenek

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

böyle kullan

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. sınıflarını basit tutar
  2. esneklik için fabrikalara sunulan enjeksiyon

0

Ben şahsen @Ingo Bürk'ün 3. seçeneğini tercih ediyorum. Ve bir dizi karmaşık veriyi ve ilkel veri dizisini desteklemek için kodlarını geliştirdim.

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

aşağıdaki gibi yapabilirsiniz

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

ve

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

Bu aslında beklenen nesne türünüzün bir çalışma zamanı örneği üretmez. Türünüz yalnızca ilkel özelliklere sahip olduğunda çalışır, ancak bir türün yöntemleri olduğunda başarısız olur. Arabirim tanımları çalışma zamanında da kullanılamaz (yalnızca derleme süresi).
Todd

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

içeriği aşağıdaki gibi arayın:
user8390810

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

Bu, "sınıfları gerektiği gibi başlatır" gibi görünmüyor.
LexieHankins
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.