मैं JSON ऑब्जेक्ट के साथ टाइपस्क्रिप्ट ऑब्जेक्ट को इनिशियलाइज़ कैसे करूँ


198

मुझे AJAX कॉल से REST सर्वर पर एक JSON ऑब्जेक्ट प्राप्त होता है। इस ऑब्जेक्ट में प्रॉपर्टी के नाम हैं जो मेरे टाइपस्क्रिप्ट वर्ग से मेल खाते हैं (यह इस प्रश्न का अनुसरण है )।

इसे शुरू करने का सबसे अच्छा तरीका क्या है? मुझे नहीं लगता कि यह काम करेगा क्योंकि वर्ग (और JSON ऑब्जेक्ट) में सदस्य हैं जो ऑब्जेक्ट्स की सूची और सदस्य हैं जो वर्ग हैं, और उन कक्षाओं में सदस्य हैं जो सूची और / या वर्ग हैं।

लेकिन मैं एक दृष्टिकोण पसंद करूँगा जो सदस्य के नामों को देखता है और उन्हें असाइन करता है, आवश्यकतानुसार सूचियाँ और तात्कालिक कक्षाएं बनाता है, इसलिए मुझे हर वर्ग के प्रत्येक सदस्य के लिए स्पष्ट कोड लिखने की ज़रूरत नहीं है (वहाँ बहुत है!)


1
आपने इसे फिर से क्यों पूछा (जैसा कि मैंने दूसरे प्रश्न में प्रदान किए गए उत्तर ने कहा कि यह काम नहीं करेगा और यह एक मौजूदा वस्तु में गुणों की नकल करने के बारे में था)?
वायर्डपाइरी

1
डुप्लिकेट कैसे
डालूं

3
@WiredPrairie यह सवाल अलग है, यह पूछ रहा है कि क्या मैं एक-एक करके संपत्तियों को चला सकता हूं और उन्हें भर में सौंप सकता हूं। अन्य प्रश्न पूछ रहे थे कि क्या मैं इसे डाल सकता हूं।
डेविड थिएलेन

1
@WiredPrairie कॉन्टेस्ट: यदि आप प्राइमरी प्रकार तक प्राप्त करने तक गुणों में गोताखोरी करते रहते हैं, तो उन्हें सौंपा जा सकता है।
डेविड थिएलेन

2
यह अभी भी सभी मूल्यों की नकल कर रहा है जैसा कि मैंने सुझाव दिया था कि आपको क्या करना होगा। टाइपस्क्रिप्ट में ऐसा करने का कोई नया तरीका नहीं है क्योंकि यह जावास्क्रिप्ट का एक मौलिक डिज़ाइन है। बड़ी वस्तुओं के लिए, आप किसी भी मान को कॉपी नहीं करना चाहते हैं और इसके बजाय डेटा संरचना पर "कार्य" कर सकते हैं।
वायर्डप्रैरी

जवाबों:


188

यह कुछ अलग तरीके दिखाने के लिए इस पर कुछ त्वरित शॉट्स हैं। वे किसी भी तरह से "पूर्ण" नहीं हैं और एक अस्वीकरण के रूप में, मुझे नहीं लगता कि यह ऐसा करना एक अच्छा विचार है। इसके अलावा कोड बहुत साफ नहीं है क्योंकि मैंने इसे जल्दी से एक साथ टाइप किया था।

नोट के रूप में भी: निश्चित रूप से डिसेरिएबल क्लासेस के लिए डिफॉल्ट कंस्ट्रक्टर की आवश्यकता होती है जैसा कि अन्य सभी भाषाओं में होता है, जहाँ मुझे किसी भी तरह के डीरियलाइज़ेशन की जानकारी है। बेशक, अगर आप बिना किसी तर्क के एक गैर-डिफ़ॉल्ट निर्माता को कॉल करते हैं, तो जावास्क्रिप्ट शिकायत नहीं करेगा, लेकिन कक्षा को इसके लिए बेहतर तैयार किया जाएगा (इसके अलावा, यह वास्तव में "टाइपस्क्रिप्ट तरीका" नहीं होगा)।

विकल्प # 1: कोई रन-टाइम जानकारी बिल्कुल नहीं

इस दृष्टिकोण के साथ समस्या यह है कि किसी भी सदस्य का नाम उसकी कक्षा से मेल खाना चाहिए। जो स्वचालित रूप से आपको प्रति वर्ग एक ही प्रकार के एक सदस्य तक सीमित करता है और अच्छे अभ्यास के कई नियमों को तोड़ता है। मैं इसके खिलाफ दृढ़ता से सलाह देता हूं, लेकिन बस इसे यहां सूचीबद्ध करें क्योंकि यह पहला "मसौदा" था जब मैंने यह उत्तर लिखा था (यह भी कि नाम "फू" आदि क्यों हैं)।

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: नाम संपत्ति

विकल्प # 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: क्रिया, लेकिन साफ-सुथरा तरीका

अपडेट 01/03/2016: जैसा कि @GameAlchemist ने टिप्पणियों ( विचार , कार्यान्वयन) में बताया ) टाइपस्क्रिप्ट 1.7 के रूप में, नीचे वर्णित समाधान को क्लास / प्रॉपर्टी डेकोरेटर्स का उपयोग करके बेहतर तरीके से लिखा जा सकता है।

सीरियलाइजेशन हमेशा एक समस्या है और मेरी राय में, सबसे अच्छा तरीका एक तरीका है जो सिर्फ सबसे छोटा नहीं है। सभी विकल्पों में से, यह वही है जो मैं पसंद करूंगा क्योंकि वर्ग के लेखक का वांछित वस्तुओं की स्थिति पर पूर्ण नियंत्रण है। अगर मुझे अनुमान लगाना था, तो मैं कहूंगा कि अन्य सभी विकल्प, जल्दी या बाद में, आपको परेशानी में डाल देंगे (जब तक कि जावास्क्रिप्ट इस से निपटने के लिए एक देशी तरीका नहीं आता है)।

वास्तव में, निम्न उदाहरण लचीलापन न्याय नहीं करता है। यह वास्तव में सिर्फ कक्षा की संरचना की नकल करता है। हालांकि, यहां आपको जो अंतर रखना है, वह यह है कि किसी भी प्रकार के 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 वह है जो मैं जाने के लिए एक उचित तरीका कहूंगा। आपको अभी भी डिसेरिएलाइज़ेशन कोड लिखने की आवश्यकता है, लेकिन यह उसी कक्षा में है और पूरी तरह से चलाया जा सकता है। यदि आप जावा से आ रहे हैं, तो यह लिखने equalsया toStringविधियों (केवल यह है कि आप आमतौर पर उन्हें ऑटो-जेनरेट किया गया है) होने के लिए तुलनीय है । यदि आप चाहते हैं तो इसके लिए जनरेटर लिखना बहुत कठिन नहीं होना चाहिए deserialize, लेकिन यह केवल रन-टाइम स्वचालन नहीं हो सकता है।
इंगो बुर्क

2
@ IngoBürk, मुझे पता है कि मैं यह सवाल 2 साल बाद पूछ रहा हूं, लेकिन वस्तुओं की सरणी पर यह कैसे काम करेगा? ऊपर का नमूना कोड JSON ऑब्जेक्ट के लिए ठीक काम करता है। यह वस्तुओं की सरणी के लिए कैसे उपयोग किया जा सकता है?
प्रतीकात्मक गायकवाड़

2
एक पक्ष टिप्पणी: 1.7 के बाद से, (आपके जवाब की तुलना में हाल ही में अधिक), टाइपस्क्रिप्ट कक्षा / संपत्ति सज्जाकार प्रदान करता है जो 4 वें समाधान को एक नीच तरीके से लिखने की अनुमति देता है।
गेमअल्केमिस्ट

1
मैंने पाया सबसे अच्छा प्रलेखन एक StackOverflow जवाब है: stackoverflow.com/a/29837695/856501 । मैं अपने एक प्रोजेक्ट में डेकोरेटर्स का इस्तेमाल करता था, और हालाँकि मुझे कुछ अन्य फीचर्स चाहिए जो मुझे कहना है कि वे एक आकर्षण की तरह काम करते हैं।
गेमअल्केमिस्ट

2
मैं अभी तक एक प्रोडक्शन प्रोजेक्ट के लिए डेकोरेटर्स पर नहीं कूदता - ध्यान रखें कि वे अभी भी एक प्रायोगिक विशेषता हैं। मैं "प्रयोगों" पर वास्तविक दुनिया कोड को आधार नहीं बनाऊंगा क्योंकि जहां तक ​​हमारा सवाल है कि वे अगले संस्करण में जा सकते हैं और आपको कोड का एक गुच्छा फिर से लिखना होगा या हमेशा के लिए एक पुराने टीएस संस्करण पर अटक जाना होगा। बस मेरे $ .02
आरवीपी

35

आप इसका उपयोग कर सकते हैं Object.assignमुझे नहीं पता कि यह कब जोड़ा गया था, मैं वर्तमान में टाइपस्क्रिप्ट 2.0.2 का उपयोग कर रहा हूं, और यह ईएस 6 फीचर प्रतीत होता है।

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:। हम एक के ऊपर दो लेक्सिकॉन जैसे उत्तर क्यों देते हैं?
फिल्पा ४

18
@Blauhim क्योंकि Object.assignपुनरावर्ती रूप से काम नहीं करेगा, और न ही सही वस्तु प्रकारों को त्वरित करना , मूल्यों को Objectउदाहरणों के रूप में छोड़ना । जबकि यह तुच्छ कार्यों के लिए ठीक है, इसके साथ जटिल प्रकार का क्रमांकन संभव नहीं है। उदाहरण के लिए, यदि एक वर्ग संपत्ति एक कस्टम वर्ग प्रकार की है, तो JSON.parse+ Object.assignउस संपत्ति को तुरंत भेज देगी Object। साइड-इफेक्ट्स में लापता तरीके और एक्सेसर्स शामिल हैं।
जॉन वीज़

@JohnWeisz ऑब्जेक्ट असाइन के शीर्ष स्तर के वर्ग का सही प्रकार है, और मैंने इसमें पुनरावर्ती बात का उल्लेख किया है ... जो कहा, YMMV, और वे डील ब्रेकर हो सकते हैं।
xenoterracide

सीधे प्रश्न से उद्धृत: "वर्ग में सदस्य होते हैं जो वस्तुओं की सूची और सदस्य होते हैं जो वर्ग होते हैं, और उन कक्षाओं में सदस्य होते हैं जो सूची और / या वर्ग होते हैं [...] मैं एक दृष्टिकोण पसंद करूंगा जो सदस्य दिखता है नाम और उन्हें असाइन करना, आवश्यकतानुसार सूचियाँ बनाना और तात्कालिक बनाना, इसलिए मुझे हर कक्षा में हर सदस्य के लिए स्पष्ट कोड लिखना नहीं है " - ऐसा नहीं है Object.assign, जहाँ यह अभी भी नेस्टेड इंस्टीट्यूशन लिखने के लिए नीचे आता है हाथ। यह दृष्टिकोण बहुत ही सरल, ट्यूटोरियल स्तर की वस्तुओं के लिए ठीक है, लेकिन वास्तविक उपयोग के लिए नहीं।
जॉन वेइसज

@JohnWeisz यकीन है, ज्यादातर इस के साथ जवाब दिया क्योंकि यह किसी भी जवाब में नहीं था और कुछ उपयोग मामलों के लिए सरल लग रहा था। मुझे यकीन है कि इसे अन्य उत्तरों जैसे कि प्रतिबिंब के साथ संयोजन में भी इस्तेमाल किया जा सकता है, जो आप देख रहे हैं। मैंने इसे भाग में भी लिखा था ताकि मैं इसे बाद में याद रखूँ। इन जवाबों को देखते हुए और उपयोग किए जाने वाले और अधिक शक्तिशाली पुस्तकालयों को "वास्तविक उपयोग" के लिए उपलब्ध कुछ भी प्रतीत नहीं होता है।
xenoterracide

34

TLDR: TypedJSON (अवधारणा का काम करने वाला प्रमाण)


इस समस्या की जड़ की जड़ यह है कि हमें JSON को रनटाइम पर डिसेररलाइज करने की जरूरत है ताकि टाइप जानकारी का उपयोग किया जा सके जो केवल कंपाउंड समय पर मौजूद है । इसके लिए आवश्यक है कि टाइप-इनफॉर्मेशन किसी तरह रनटाइम पर उपलब्ध कराई जाए।

सौभाग्य से, यह सज्जाकारों और ReflectDecorators के साथ एक बहुत ही सुंदर और मजबूत तरीके से हल किया जा सकता है :

  1. संपत्तियों पर संपत्ति सज्जाकारों का उपयोग करें, जो क्रमबद्धता के अधीन हैं, मेटाडेटा जानकारी रिकॉर्ड करने और उस जानकारी को कहीं स्टोर करने के लिए, उदाहरण के लिए कक्षा के प्रारूप पर
  2. इस मेटाडेटा जानकारी को एक पुनरावर्ती आरोग्यलाभ (deserializer) को खिलाएं

 

रिकॉर्डिंग प्रकार-सूचना

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 का विरोध करता है ।

 

एज केस

हालांकि, यदि आप अब खुश हैं कि समाधान इतना आसान है, तो मेरे पास कुछ बुरी खबरें हैं: बड़ी संख्या में किनारे मामले हैं जिनकी देखभाल करने की आवश्यकता है। जिनमें से कुछ ही हैं:

  • सरणी और सरणी तत्व (विशेषकर नेस्टेड सरणियों में)
  • बहुरूपता
  • सार वर्ग और इंटरफेस
  • ...

यदि आप इन सभी के साथ इधर-उधर नहीं फटकना चाहते (मैं शर्त लगाता हूं), मैं इस दृष्टिकोण का उपयोग करते हुए प्रूफ-ऑफ-कॉन्सेप्ट के काम करने वाले प्रायोगिक संस्करण की सिफारिश करने में प्रसन्नता महसूस करूंगा , जो मैंने बनाया था इस सटीक समस्या से निपटने के लिए, मैं एक समस्या का सामना खुद रोज करता हूं।

डेकोरेटर अभी भी कैसे प्रायोगिक माने जा रहे हैं, मैं इसे उत्पादन के उपयोग के लिए उपयोग करने की सलाह नहीं दूंगा, लेकिन अभी तक इसने मुझे अच्छी तरह से परोसा है।


टाइप्डसन ने काम किया; संदर्भ के लिए बहुत बहुत धन्यवाद।
नील

महान काम, आप एक समस्या के लिए बहुत ही सुरुचिपूर्ण समाधान के साथ आए हैं जो मुझे थोड़ी देर के लिए परेशान कर रहा है। मैं आपके प्रोजेक्ट का बहुत बारीकी से पालन करूंगा!
जॉन स्ट्रिकलर

12

मैं इस आदमी को काम करने के लिए इस्तेमाल कर रहा हूं: https://github.com/weichx/cerialize

यह बहुत सरल है, फिर भी शक्तिशाली है। यह समर्थन करता है:

  • वस्तुओं के एक पूरे वृक्ष का सीरियलाइजेशन और डिसरियलाइजेशन।
  • एक ही वस्तु पर लगातार और क्षणिक गुण।
  • हुक (डी) क्रमांकन तर्क को अनुकूलित करने के लिए।
  • यह एक मौजूदा उदाहरण (कोणीय के लिए महान) या नए उदाहरण उत्पन्न कर सकता है।
  • आदि।

उदाहरण:

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

मैंने एक उपकरण बनाया है जो टाइपस्क्रिप्ट इंटरफेस और रनटाइम टाइपकेक करने के लिए एक रनटाइम "टाइप मैप" बनाता है JSON.parse: ts.quicktype.io

उदाहरण के लिए, यह JSON दिया गया:

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

quicktype निम्न टाइपस्क्रिप्ट इंटरफ़ेस और टाइप मैप तैयार करता है:

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

मैंने कुछ कोड छोड़ दिए हैं, लेकिन आप विवरण के लिए क्विकटाइप आज़मा सकते हैं ।


1
कई घंटों के शोध के बाद और पार्सिंग तकनीकों के एक जोड़े पर अपना हाथ आजमाने के बाद, मैं कह सकता हूं कि यह एक उत्कृष्ट समाधान है - मुख्यतः क्योंकि सज्जाकार अभी भी प्रायोगिक हैं। * मेरे लिए मूल लिंक टूट गया है; लेकिन ts.quicktype.io काम करता है। * JSON को JSON स्कीमा में बदलना एक अच्छा पहला कदम है।
लेक्सीहैंकिंस

3

विकल्प # 5: टाइपस्क्रिप्ट कंस्ट्रक्टर और 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;
    }
}

आपके अजाक्स कॉलबैक में जहां आपको वेतन की गणना करने के लिए एक कंपनी मिलती है:

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

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

कहा $.extendसे आये
whale_steward

@whale_steward मुझे लगता है कि लेखक jQuery पुस्तकालय की बात कर रहा है। जावास्क्रिप्ट दुनिया में, '$' बहुत बार किसी ने jQuery का उपयोग किया है।
निक रोथ

इसे कैसे आयात करें? बस html सिर पर इसे शामिल करना पर्याप्त है?
whale_steward

हाँ मैं jQuery द्वारा $ को बदलने के उत्तर को अपडेट करता हूं। html सिर में jQuery.js आयात करें, और अपने पैकेज में @ प्रकार / jquery को स्थापित करें और जोड़ें। json, devDependence सेक्शन।
एंथनी ब्रेनेलीयर

1
ध्यान दें कि जावास्क्रिप्ट में, आपको ऐसा करना चाहिए Object.assign, जो इस निर्भरता को jQuery से दूर करता है।
लेओन पेलेटियर

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 वें विकल्प के साथ जोड़ा जाना है, जहां आपको एक वर्ग पदानुक्रम को संभालना होगा, उदाहरण के लिए सदस्य सूची, जो उपवर्गों में से किसी एक में से एक है एक सदस्य सुपर क्लास, जैसे निदेशक सदस्य का विस्तार करता है या छात्र सदस्य का विस्तार करता है। उस स्थिति में आपको उप-प्रकार के प्रकार को json प्रारूप में देना होगा


1

JQuery का। यह आपके लिए करता है:

var mytsobject = new mytsobject();

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

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

1

इस उद्देश्य के लिए मुझे जो सबसे अच्छा मिला वह है क्लास-ट्रांसफार्मर। 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

शायद वास्तविक नहीं, लेकिन सरल समाधान:

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

मैं व्यक्तिगत रूप से @Ingo Bü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

यह "[तात्कालिक] वर्गों को आवश्यकतानुसार नहीं लगता"।
लेक्सीहैंकिंस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.