पार्स जेन्सन स्ट्रिंग जावास्क्रिप्ट में एक विशेष वस्तु प्रोटोटाइप में


173

मुझे पता है कि JSON स्ट्रिंग को पार्स कैसे करें और इसे जावास्क्रिप्ट ऑब्जेक्ट में बदल दें। आप JSON.parse()आधुनिक ब्राउज़रों (और IE9 +) में उपयोग कर सकते हैं ।

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

उदाहरण के लिए, मान लीजिए कि आपके पास:

function Foo()
{
   this.a = 3;
   this.b = 2;
   this.test = function() {return this.a*this.b;};
}
var fooObj = new Foo();
alert(fooObj.test() ); //Prints 6
var fooJSON = JSON.parse({"a":4, "b": 3});
//Something to convert fooJSON into a Foo Object
//....... (this is what I am missing)
alert(fooJSON.test() ); //Prints 12

फिर, मैं यह नहीं सोच रहा हूं कि जेन्स स्ट्रिंग को जेनेरिक जावास्क्रिप्ट ऑब्जेक्ट में कैसे परिवर्तित किया जाए। मैं जानना चाहता हूं कि JSON स्ट्रिंग को "फू" ऑब्जेक्ट में कैसे परिवर्तित किया जाए। अर्थात्, मेरे ऑब्जेक्ट में अब एक फ़ंक्शन 'टेस्ट' और गुण 'a' और 'b' होना चाहिए।

अद्यतन कुछ शोध करने के बाद, मैंने इस बारे में सोचा ...

Object.cast = function cast(rawObj, constructor)
{
    var obj = new constructor();
    for(var i in rawObj)
        obj[i] = rawObj[i];
    return obj;
}
var fooJSON = Object.cast({"a":4, "b": 3}, Foo);

क्या वह काम करेगा?

अद्यतन मई, 2017 : ऐसा करने का "आधुनिक" तरीका है Object.assign, लेकिन यह फ़ंक्शन IE 11 या पुराने एंड्रॉइड ब्राउज़र में उपलब्ध नहीं है।


जवाबों:


124

वर्तमान उत्तरों में बहुत सारे हाथ से लुढ़का हुआ या लाइब्रेरी कोड है। यह आवश्यक नहीं है।

  1. JSON.parse('{"a":1}')एक सादी वस्तु बनाने के लिए उपयोग करें ।

  2. प्रोटोटाइप सेट करने के लिए मानकीकृत कार्यों में से एक का उपयोग करें:

    • Object.assign(new Foo, { a: 1 })
    • Object.setPrototypeOf({ a: 1 }, Foo.prototype)

2
Object.assign IE और पुराने Android ब्राउज़रों सहित पुराने ब्राउज़रों में उपलब्ध नहीं है। kangax.github.io/compat-table/es6/…
बीएमनर

5
उपयोग करने के खिलाफ एक बड़ी चेतावनी भी है Object.setPrototypeOf(...)developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
christo8989

@SimonEpskamp वह कोड काम नहीं करता है। अपने url की जांच करें, setPrototypeOfसंपत्ति विवरणक के लिए दूसरा पैरामीटर है ।
एरिक वैन वेलजेन

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

2
नीचे दिए गए मेरे समाधान की जाँच करें जो Object.assign (..) पर लागू होता है जो पुनरावर्ती रूप से संपत्तियों को स्वचालित रूप से हल कर सकता है (अग्रिम में प्रदान की गई थोड़ी जानकारी के साथ)
हमें

73

नीचे एक उदाहरण देखें (यह उदाहरण मूल JSON ऑब्जेक्ट का उपयोग करता है)। मेरे परिवर्तनों को CAPITALS में टिप्पणी की गई है:

function Foo(obj) // CONSTRUCTOR CAN BE OVERLOADED WITH AN OBJECT
{
    this.a = 3;
    this.b = 2;
    this.test = function() {return this.a*this.b;};

    // IF AN OBJECT WAS PASSED THEN INITIALISE PROPERTIES FROM THAT OBJECT
    for (var prop in obj) this[prop] = obj[prop];
}

var fooObj = new Foo();
alert(fooObj.test() ); //Prints 6

// INITIALISE A NEW FOO AND PASS THE PARSED JSON OBJECT TO IT
var fooJSON = new Foo(JSON.parse('{"a":4,"b":3}'));

alert(fooJSON.test() ); //Prints 12

मुझे लगता है कि आप इसके "विपरीत" भी कर सकते थे। एक रिक्त Foo ऑब्जेक्ट का निर्माण और नए Foo ऑब्जेक्ट में fooJSON से गुणों की प्रतिलिपि बनाएँ। अंत में, FooJSON को Foo ऑब्जेक्ट पर इंगित करने के लिए सेट करें।
बीएमनर

8
यह बहुत खतरनाक है। यदि ओब्ज में एक विशेषता है जो फू परिभाषा में नहीं है, तो आप एक अतिरिक्त छिपी हुई संपत्ति के साथ एक फू ऑब्जेक्ट बनाएंगे जो आपको इसका नाम नहीं पता है ... एक लूप के बजाय मैं बस करूँगा: this.a = obj। a and this.b = obj.b. या सीधे मैं "a" और "b" को मापदंडों के रूप में पास करूँगा: नया फू (obj.a, obj.b)
गेब्रियल

2
गगलेकास की सलाह सुनने लायक है। (हालांकि "बहुत खतरनाक है" थोड़ा ओटीटी है।) उदाहरण ऊपर है बस आपको एक विचार देना है। सही क्रियान्वयन आपके आवेदन पर निर्भर करेगा।
ओलिवर मोरन

11
आप स्वयं को प्रोटोटाइप गुणों से बचाना चाहते हैं। for (var prop in obj) {if (obj.hasOwnProperty(prop)) {this[prop] = obj[prop];}}
रोमेन वेगनरी

3
@RomainVergnory के लिए और भी अधिक सुरक्षा, मैं केवल इनिशियलाइज़ निर्माता में बनाया गुण, obj के बजाय इस: for (var prop in obj) {if (this.hasOwnProperty(prop)) {this[prop] = obj[prop];}}। यह मानता है कि आप सर्वर को सभी गुणों को आबाद करने की उम्मीद करते हैं, IMO को भी फेंक देना चाहिए अगर obj.hasOwnProperty () विफल रहता है ...
tekHedd

42

क्या आप JSON क्रमांकन / deserialization कार्यक्षमता जोड़ना चाहते हैं, है ना? फिर इसे देखें:

आप इसे हासिल करना चाहते हैं:

यूएमएल

toJson () एक सामान्य तरीका है।
fromJson () एक स्थिर विधि है।

कार्यान्वयन :

var Book = function (title, author, isbn, price, stock){
    this.title = title;
    this.author = author;
    this.isbn = isbn;
    this.price = price;
    this.stock = stock;

    this.toJson = function (){
        return ("{" +
            "\"title\":\"" + this.title + "\"," +
            "\"author\":\"" + this.author + "\"," +
            "\"isbn\":\"" + this.isbn + "\"," +
            "\"price\":" + this.price + "," +
            "\"stock\":" + this.stock +
        "}");
    };
};

Book.fromJson = function (json){
    var obj = JSON.parse (json);
    return new Book (obj.title, obj.author, obj.isbn, obj.price, obj.stock);
};

उपयोग :

var book = new Book ("t", "a", "i", 10, 10);
var json = book.toJson ();
alert (json); //prints: {"title":"t","author":"a","isbn":"i","price":10,"stock":10}

var book = Book.fromJson (json);
alert (book.title); //prints: t

नोट: यदि आप आप की तरह सभी संपत्ति परिभाषाएँ बदल सकते हैं this.title, this.authorआदि द्वारा var title, var authorआदि और उन्हें ही टिककर खेल जोड़ने यूएमएल परिभाषा को पूरा करने के।


4
मैं सहमत हूँ। यह कार्यान्वयन निश्चित रूप से काम करेगा, और यह बहुत अच्छा है ... बुक ऑब्जेक्ट के लिए बस थोड़ी सी चिंता और विशिष्ट। IMHO, JS की शक्ति प्रोटोटाइप से आती है और यदि आप चाहते हैं तो कुछ अतिरिक्त गुण होने की क्षमता है। बस इतना ही कह रहा हूं। ' मैं वास्तव में वन-लाइनर की तलाश में था: x .__ proto__ = X.prototype; (हालांकि यह इस समय IE ब्राउज़र संगत नहीं है)
बीएमनर

4
यह मत भूलो कि आपकी toJson()पद्धति - चाहे वह अलग-अलग गुण हार्डकोड हो या प्रत्येक के लिए उपयोग करता है - को कुछ वर्णों के लिए बैकस्लैश एस्केप कोड जोड़ने की आवश्यकता होगी जो प्रत्येक स्ट्रिंग संपत्ति में हो सकते हैं। (उदाहरण के लिए एक पुस्तक का शीर्षक उद्धरण चिह्न हो सकता है।)
nnnnnn

1
हां, मुझे पता है, मेरा जवाब एक उदाहरण और सवाल के लिए सबसे अच्छा जवाब था, लेकिन ... एक सकारात्मक बिंदु भी नहीं ... मुझे नहीं पता कि मैं अपना समय दूसरों की मदद करने में क्यों बर्बाद कर रहा हूं
गेब्रियल लामास

7
इन दिनों मैं JSON.stringify()स्वयं toJSon () लिखने के बजाय उपयोग करूँगा। अब पहिया को सुदृढ़ करने की आवश्यकता नहीं है कि सभी आधुनिक ब्राउज़र इसका समर्थन करते हैं।
पत्थर

2
@Skypecakes के साथ सहमत हुए। यदि आप केवल संपत्तियों के सबसेट को क्रमबद्ध करना चाहते हैं, तो क्रमबद्ध गुणों की एक निरंतरता बनाएं। serializable = ['title', 'author', ...]JSON.stringify(serializable.reduce((obj, prop) => {...obj, [prop]: this[prop]}, {}))
एटिकस

19

एक ब्लॉग पोस्ट जो मुझे उपयोगी लगी : जावास्क्रिप्ट प्रोटोटाइप को समझना

आप ऑब्जेक्ट की __proto__ संपत्ति के साथ गड़बड़ कर सकते हैं।

var fooJSON = jQuery.parseJSON({"a":4, "b": 3});
fooJSON.__proto__ = Foo.prototype;

यह fooJSON को फू प्रोटोटाइप को इनहेरिट करने की अनुमति देता है।

मुझे नहीं लगता कि यह IE में काम करता है, हालांकि ... कम से कम मैंने जो पढ़ा है।


2
दरअसल, कुछ ऐसा ही मेरा पहला वृत्ति था।
ओलिवर मोरन

14
ध्यान दें कि __proto__लंबे समय से पदावनत किया गया है । इसके अलावा, प्रदर्शन कारणों से, यह पहले से ही बनाई गई वस्तु [ __proto__किसी भी तरह से या किसी अन्य साधन से ] [[प्रोटोटाइप]] को संशोधित करने की अनुशंसा नहीं की जाती है ।
यू असाकुसा

1
काश, वास्तव में गैर-पदावनत समाधानों में से कोई भी इससे अधिक जटिल नहीं है ...
विम लेयर्स

मैंने परिवर्तन के प्रदर्शन के कुछ परीक्षण किए हैं [[prototype]]और यह क्रोम में अप्रासंगिक लगता है। फ़ायरफ़ॉक्स में नई कॉलिंग प्रोटोटाइप का उपयोग करने की तुलना में धीमी है, और Object.create सबसे तेज़ है। मुझे लगता है कि एफएफ के साथ मुद्दा यह है कि पहला परीक्षण पिछले की तुलना में धीमा है, निष्पादन मामलों का सिर्फ आदेश। क्रोम में सब कुछ लगभग एक ही गति से चलता है। मेरा मतलब है कि संपत्ति का उपयोग और तरीकों की एनवोकेशन। क्रिएटिन नए के साथ तेज है, लेकिन यह इतना महत्वपूर्ण नहीं है। देखें: jsperf.com/prototype-change-test-8874874/1 और: jsperf.com/prototype-changed-method-call
Bogdan मार्ट

4
मुझे लगता है कि इन दिनों, एक Object.setPrototypeOf(fooJSON, Foo.prototype)सेटिंग के बजाय फोन करेगा fooJSON.__proto__... है ना?
stakx - अब

11

क्या मुझे इस सवाल में कुछ याद आ रहा है या किसी और ने 2011 reviverके JSON.parseबाद से पैरामीटर का उल्लेख क्यों नहीं किया ?

यहाँ समाधान के लिए सरलीकृत कोड है जो काम करता है: https://jsfiddle.net/Ldr2utrr/

function Foo()
{
   this.a = 3;
   this.b = 2;
   this.test = function() {return this.a*this.b;};
}


var fooObj = new Foo();
alert(fooObj.test() ); //Prints 6
var fooJSON = JSON.parse(`{"a":4, "b": 3}`, function(key,value){
if(key!=="") return value; //logic of course should be more complex for handling nested objects etc.
  let res = new Foo();
  res.a = value.a;
  res.b = value.b;
  return res;
});
// Here you already get Foo object back
alert(fooJSON.test() ); //Prints 12

पुनश्च: आपका प्रश्न भ्रामक है: >> यह बहुत अच्छा है, लेकिन मैं उस जावास्क्रिप्ट ऑब्जेक्ट को कैसे ले सकता हूं और इसे एक विशेष जावास्क्रिप्ट ऑब्जेक्ट (यानी एक निश्चित प्रोटोटाइप के साथ) में बदल सकता हूं? शीर्षक के विपरीत, जहाँ आप JSON पार्सिंग के बारे में पूछते हैं, लेकिन उद्धृत पैराग्राफ JS रनटाइम ऑब्जेक्ट प्रोटोटाइप प्रतिस्थापन के बारे में पूछता है।


3

एक वैकल्पिक दृष्टिकोण का उपयोग किया जा सकता है Object.create। पहले तर्क के रूप में, आप प्रोटोटाइप को पास करते हैं, और दूसरे के लिए आप विवरणकर्ताओं को संपत्ति के नाम का एक नक्शा देते हैं:

function SomeConstructor() {
  
};

SomeConstructor.prototype = {
  doStuff: function() {
      console.log("Some stuff"); 
  }
};

var jsonText = '{ "text": "hello wrold" }';
var deserialized = JSON.parse(jsonText);

// This will build a property to descriptor map
// required for #2 argument of Object.create
var descriptors = Object.keys(deserialized)
  .reduce(function(result, property) {
    result[property] = Object.getOwnPropertyDescriptor(deserialized, property);
  }, {});

var obj = Object.create(SomeConstructor.prototype, descriptors);


3

मुझे कंस्ट्रक्टर और कॉलिंग में एक वैकल्पिक तर्क जोड़ना पसंद है Object.assign(this, obj), फिर किसी भी गुण को संभालना जो ऑब्जेक्ट्स या ऑब्जेक्ट्स के एरेज़ हैं:

constructor(obj) {
    if (obj != null) {
        Object.assign(this, obj);
        if (this.ingredients != null) {
            this.ingredients = this.ingredients.map(x => new Ingredient(x));
        }
    }
}

2

पूर्णता की खातिर, यहाँ एक सरल एक-लाइनर के साथ मैं समाप्त हो गया (मुझे गैर-फू-गुणों की जाँच की कोई आवश्यकता नहीं थी):

var Foo = function(){ this.bar = 1; };

// angular version
var foo = angular.extend(new Foo(), angular.fromJson('{ "bar" : 2 }'));

// jquery version
var foo = jQuery.extend(new Foo(), jQuery.parseJSON('{ "bar" : 3 }'));

2

मैंने एक पैकेज बनाया, जिसे json-dry कहा जाता है । यह (परिपत्र) संदर्भों का समर्थन करता है और वर्ग उदाहरण भी।

आपको अपनी कक्षा में ( toDryप्रोटोटाइप के unDryरूप में और स्थैतिक विधि के रूप में) 2 नई विधियों को परिभाषित करना होगा , कक्षा को पंजीकृत करना होगा ( Dry.registerClass), और जाने से पहले।


1

हालांकि, यह तकनीकी रूप से आप क्या चाहते हैं, यदि आप हाथ से पहले जानते हैं कि आप किस प्रकार की वस्तु को संभालना चाहते हैं, तो आप अपने ज्ञात ऑब्जेक्ट के प्रोटोटाइप के कॉल / आवेदन के तरीकों का उपयोग कर सकते हैं।

आप इसे बदल सकते हैं

alert(fooJSON.test() ); //Prints 12

इसके लिए

alert(Foo.prototype.test.call(fooJSON); //Prints 12

1

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

एक धारणा यह है कि आपने एक विशेष फाइल को परिभाषित किया है जो यह इंगित करता है कि यह प्रत्येक वस्तु में टाइप है जिसे आप लागू करना चाहते हैं यह स्वचालित रूप से ( this.__typeउदाहरण में) है।

function Msg(data) {
    //... your init code
    this.data = data //can be another object or an array of objects of custom types. 
                     //If those objects defines `this.__type', their types will be assigned automatically as well
    this.__type = "Msg"; // <- store the object's type to assign it automatically
}

Msg.prototype = {
    createErrorMsg: function(errorMsg){
        return new Msg(0, null, errorMsg)
    },
    isSuccess: function(){
        return this.errorMsg == null;
    }
}

उपयोग:

var responseMsg = //json string of Msg object received;
responseMsg = assignType(responseMsg);

if(responseMsg.isSuccess()){ // isSuccess() is now available
      //furhter logic
      //...
}

प्रकार असाइनमेंट फ़ंक्शन (यह किसी भी नेस्टेड ऑब्जेक्ट्स को प्रकार असाइन करने के लिए पुनरावर्ती कार्य करता है; यह किसी भी उपयुक्त ऑब्जेक्ट को खोजने के लिए सरणियों के माध्यम से भी पुनरावृत्त करता है):

function assignType(object){
    if(object && typeof(object) === 'object' && window[object.__type]) {
        object = assignTypeRecursion(object.__type, object);
    }
    return object;
}

function assignTypeRecursion(type, object){
    for (var key in object) {
        if (object.hasOwnProperty(key)) {
            var obj = object[key];
            if(Array.isArray(obj)){
                 for(var i = 0; i < obj.length; ++i){
                     var arrItem = obj[i];
                     if(arrItem && typeof(arrItem) === 'object' && window[arrItem.__type]) {
                         obj[i] = assignTypeRecursion(arrItem.__type, arrItem);
                     }
                 }
            } else  if(obj && typeof(obj) === 'object' && window[obj.__type]) {
                object[key] = assignTypeRecursion(obj.__type, obj);
            }
        }
    }
    return Object.assign(new window[type](), object);
}

0

वर्तमान में स्वीकृत उत्तर मेरे लिए काम नहीं कर रहा था। आपको ठीक से Object.assign () का उपयोग करने की आवश्यकता है:

class Person {
    constructor(name, age){
        this.name = name;
        this.age = age;
    }

    greet(){
        return `hello my name is ${ this.name } and i am ${ this.age } years old`;
    }
}

आप सामान्य रूप से इस वर्ग की वस्तुएँ बनाते हैं:

let matt = new Person('matt', 12);
console.log(matt.greet()); // prints "hello my name is matt and i am 12 years old"

यदि आपके पास एक जॉगन स्ट्रिंग है, तो आपको व्यक्ति वर्ग में पार्स करने की आवश्यकता है, ऐसा करें:

let str = '{"name": "john", "age": 15}';
let john = JSON.parse(str); // parses string into normal Object type

console.log(john.greet()); // error!!

john = Object.assign(Person.prototype, john); // now john is a Person type
console.log(john.greet()); // now this works

-3

ओलिवर्स के उत्तर बहुत स्पष्ट हैं, लेकिन अगर आप कोणीय js में एक समाधान की तलाश में हैं, तो मैंने एक अच्छा मॉड्यूल लिखा है जिसे Angular-jsClass कहा जाता है, जो इस आसानी को करता है, एक बड़ी परियोजना के लिए लक्ष्य बनाते समय लिटरल अंकन में परिभाषित ऑब्जेक्ट हमेशा खराब होते हैं। लेकिन यह कहते हुए कि डेवलपर्स को समस्या का सामना करना पड़ता है, जो कि वास्तव में बीएमएनआर ने कहा है, प्रोटोटाइप या कंस्ट्रक्टर नोटेशन ऑब्जेक्ट्स के लिए एक जस्सिन को कैसे सीरियल करें

var jone = new Student();
jone.populate(jsonString); // populate Student class with Json string
console.log(jone.getName()); // Student Object is ready to use

https://github.com/imalhasaranga/Angular-JSClass

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