JavaScript'te JSON Dizesini Belirli Bir Nesne Prototipe Ayrıştırma


173

Bir JSON String ayrıştırmak ve bir JavaScript nesnesi açmak nasıl biliyorum. Sen kullanabilirsiniz JSON.parse()modern tarayıcılarda (ve IE9 +) 'de.

Bu harika, ama bu JavaScript Nesnesini alıp belirli bir JavaScript Nesnesine (yani belirli bir prototiple) nasıl dönüştürebilirim?

Örneğin, varsayalım:

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

Yine, bir JSON dizesini genel bir JavaScript nesnesine dönüştürmeyi merak etmiyorum. Bir JSON dizesini "Foo" nesnesine dönüştürmek nasıl bilmek istiyorum. Yani, Nesnem artık bir 'test' fonksiyonuna ve 'a' ve 'b' özelliklerine sahip olmalıdır.

GÜNCELLEME Biraz araştırma yaptıktan sonra bunu düşündüm ...

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

Çalışacak mı?

GÜNCELLEME Mayıs, 2017 : Bunu yapmanın "modern" yolu Object.assign, ancak bu işlev IE 11 veya daha eski Android tarayıcılarında mevcut değildir.


Yanıtlar:


124

Şu anki cevaplar bir çok elle veya kütüphane kodu içeriyor. Bu gerekli değil.

  1. JSON.parse('{"a":1}')Düz bir nesne oluşturmak için kullanın .

  2. Prototipi ayarlamak için standart işlevlerden birini kullanın:

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

2
Object.assign, IE ve daha eski Android tarayıcıları da dahil olmak üzere eski tarayıcılarda kullanılamaz. kangax.github.io/compat-table/es6/…
BMiner

5
Kullanmaya karşı da büyük bir uyarı var Object.setPrototypeOf(...). developer.mozilla.org/tr-TR/docs/Web/JavaScript/Reference/…
christo8989

@SimonEpskamp Bu kod çalışmıyor. setPrototypeOfÖzellik tanımlayıcıları olan ikinci parametre olan URL'nizi kontrol edin .
Erik van Velzen

6
Prototip ayarlaması gereken bir özellik varsa, prototip ayarlama çözümü çalışmaz. Başka bir deyişle: yalnızca birinci düzey veri hiyerarşisini çözer.
Vojta

2
özellikleri otomatik olarak çözebilen Object.assign (..) 'i tekrarlayan (aşağıda verilen bir miktar bilgi ile) aşağıdaki çözümü inceleyin
vir us

73

Aşağıdaki örneğe bakın (bu örnek yerel JSON nesnesini kullanır). Değişikliklerim SERMAYELER'de yorumlandı:

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

Sanırım bunun "karşıtını" da yapabilirsiniz. Boş bir Foo Nesnesi oluşturun ve fooJSON'daki özellikleri yeni Foo Nesnesine kopyalayın. Son olarak, fooJSON'u Foo Nesnesini gösterecek şekilde ayarlayın.
BMiner

8
Bu çok tehlikelidir. Nesnenin Foo tanımında olmayan bir özniteliği varsa, adını bilmediğiniz ekstra gizli bir özelliğe sahip bir Foo nesnesi oluşturacaksınız ... Bir döngü yerine basitçe yapacağım: this.a = obj. a ve this.b = obj.b. Veya doğrudan parametre olarak "a" ve "b" yi geçirdim: yeni Foo (obj.a, obj.b)
Gabriel Llamas

2
GagleKas'ın tavsiyesi dinlemeye değer. ("Çok tehlikeli" olmasına rağmen biraz OTT.) Yukarıdaki örnek sadece size bir fikir vermektir. Doğru uygulama, uygulamanıza bağlı olacaktır.
Oliver Moran

11
Kendinizi prototip özelliklerinden korumak isteyebilirsiniz. for (var prop in obj) {if (obj.hasOwnProperty(prop)) {this[prop] = obj[prop];}}
Romain Vergnory

3
@RomainVergnory Daha fazla güvenlik için, yalnızca obj yapıcı yerine bu yapıcıda oluşturulan özellikleri başlatırım for (var prop in obj) {if (this.hasOwnProperty(prop)) {this[prop] = obj[prop];}}. Bu, sunucunun tüm özellikleri doldurmasını beklediğinizi varsayar; obj.hasOwnProperty () başarısız olursa IMO da atmalıdır ...
tekHedd

42

JSON serileştirme / serileştirme işlevselliğini eklemek istiyor musunuz? Sonra şuna bakın:

Bunu başarmak istiyorsunuz:

UML

toJson () normal bir yöntemdir.
fromJson () statik bir yöntemdir.

Uygulama :

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

Kullanımı :

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

Not: Eğer gibi tüm mülkiyet tanımlarını değiştirebilir isterseniz this.title, this.authorvb tarafından var title, var authorvb ve UML tanımını gerçekleştirmek için onlara alıcılar ekleyin.


4
Katılıyorum. Bu uygulama kesinlikle işe yarayacak ve harika ... sadece biraz garip ve Kitap Nesnesine özgü. IMHO, JS'nin gücü prototiplerden ve isterseniz bazı ekstra özelliklere sahip olma yeteneğinden geliyor. Tüm söylediğim bu. Gerçekten tek astarı arıyordum: x .__ proto__ = X.prototype; (şu anda IE tarayıcı uyumlu olmasa da)
BMiner

4
toJson()Her bir dize özelliğinde olabilecek bazı karakterler için yönteminizin sabit kodlanmış veya her biri için a kullanmasına bakılmaksızın, ters eğik çizgi kaçış kodları eklemesi gerekeceğini unutmayın. (Örneğin, bir kitap başlığında tırnak işaretleri olabilir.)
nnnnnn

1
Evet, biliyorum, cevabım bir örnek ve soru için en iyi cevaptı, ama ... olumlu bir nokta bile değil ... Başkalarına yardım etmek için neden zamanımı harcadığımı bilmiyorum
Gabriel Llamas

7
Bugünlerde JSON.stringify()kendime toJSon () yazmak yerine kullanacağım . Tüm modern tarayıcılar tarafından desteklendiği için tekerleği yeniden icat etmeye gerek yok.
taş

2
@Skypecakes ile kabul etti. Yalnızca bir özellik alt kümesini serileştirmek istiyorsanız, serileştirilebilir özelliklerin sabitini oluşturun. serializable = ['title', 'author', ...]. JSON.stringify(serializable.reduce((obj, prop) => {...obj, [prop]: this[prop]}, {}))
Atticus

19

Yararlı bulduğum bir blog yazısı: JavaScript Prototiplerini Anlama

Nesnenin __proto__ özelliği ile uğraşabilirsiniz.

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

Bu, fooJSON'un Foo prototipini devralmasını sağlar.

Bunun IE'de çalıştığını sanmıyorum ... en azından okuduğumdan.


2
Aslında, böyle bir şey ilk içgüdümdü.
Oliver Moran

14
Not __proto__uzun olmuştur kullanımdan kaldırıldı . Ayrıca, performans nedenlerinden dolayı, önceden oluşturulmuş bir nesnenin [[Prototip]] dahili özelliğinin (ayarlanarak __proto__veya başka bir yolla) değiştirilmesi önerilmez .
Yu Asakusa

1
Ne yazık ki, kullanımdan kaldırılmamış çözümlerin hiçbiri bundan çok daha karmaşık değil…
Wim Leers

Değişimin bazı performans testlerini yaptım [[prototype]]ve Chrome'da alakasız görünüyor. Firefox'ta yeni çağırmak prototip kullanmaktan daha yavaştır ve Object.create en hızlıdır. Sanırım FF ile ilgili sorun ilk testin sondan daha yavaş olması, sadece yürütme sırası önemlidir. Kromda her şey neredeyse aynı hızda çalışır. Mülkiyet erişimi ve yöntemlerin çağrılması demek. Kreatin yenisiyle daha hızlıdır, ancak bu o kadar önemli değildir. bkz. jsperf.com/prototype-change-test-8874874/1 ve: jsperf.com/prototype-changed-method-call
Bogdan Mart

4
Sanırım bu gün, Object.setPrototypeOf(fooJSON, Foo.prototype)ayar yapmak yerine birileri arayacaktı fooJSON.__proto__... değil mi?
stakx - artık

11

Soruda bir şey eksik mi yoksa neden 2011'den beri hiç kimse reviverparametreden bahsetmedi JSON.parse?

İşte işe yarayan çözüm için basit kod: 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

Not: Sorunuz kafa karıştırıcı: >> Bu harika, ama bu JavaScript Nesnesini alıp belirli bir JavaScript Nesnesine (yani belirli bir prototiple) nasıl dönüştürebilirim? JSON ayrıştırma hakkında sorduğunuz başlıkla çelişir, ancak alıntılanan paragraf JS çalışma zamanı nesnesi prototipinin değiştirilmesini sorar.


3

Alternatif bir yaklaşım kullanılıyor olabilir Object.create. İlk argüman olarak, prototipi ve ikincisi için tanımlayıcılara bir özellik adları haritası iletirsiniz:

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

Yapıcıya isteğe bağlı bir argüman eklemek ve çağırmak Object.assign(this, obj), sonra nesnelerin kendileri veya nesnelerin dizileri olan özellikleri işleme gibi:

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

2

Tamlık uğruna, işte bitirdiğim basit bir astar, (Foo olmayan özellikleri kontrol etmeye gerek duymadım):

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 adlı bir paket oluşturdum . (Dairesel) referansları ve ayrıca sınıf örneklerini destekler.

Sınıfınızda ( toDryprototipte ve unDrystatik yöntem olarak) 2 yeni yöntem tanımlamanız, class ( ) 'ı kaydetmeniz Dry.registerClassve yola çıkmanız gerekir.


1

Bu teknik olarak istediğiniz gibi olmasa da, önceden işlemek istediğiniz nesne türünü biliyorsanız, bilinen nesnenizin prototipinin çağrı / uygulama yöntemlerini kullanabilirsiniz.

bunu değiştirebilirsin

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

buna

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

1

Bulabildiğim çözümleri derledim ve otomatik olarak özel bir nesneyi ve tüm alanlarını yinelemeli olarak ayrıştırabilen genel bir dosyaya derledim, böylece serileştirmeden sonra prototip yöntemlerini kullanabilirsiniz.

Bir varsayım, türünü otomatik olarak uygulamak istediğiniz her nesnede türünü belirten özel bir dosya tanımladığınızdır ( this.__typeörnekte).

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

kullanımı:

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

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

Tür atama işlevi (herhangi bir iç içe nesneye tür atamak için özyinelemeli çalışır; ayrıca uygun nesneleri bulmak için diziler arasında yineleme yapar):

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

Şu anda kabul edilen cevap benim için çalışmıyor. Object.assign () yöntemini düzgün kullanmanız gerekir:

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

Bu sınıfın nesnelerini normal olarak oluşturursunuz:

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

Person sınıfına ayrıştırmanız gereken bir json dizeniz varsa, bunu şu şekilde yapın:

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

Olivers cevapları çok açık, ancak açısal js'de bir çözüm arıyorsanız, bu kolaylığı yapan Angular-jsClass adlı güzel bir modül yazdım, büyük bir projeyi hedeflerken litaral notasyonda tanımlanan nesnelerin olması her zaman kötüdür ancak geliştiricilerin BMiner'ın tam olarak söylediği bir sorunla karşılaştığını söyleyerek, bir json'u prototip veya yapıcı gösterim nesnelerine nasıl serileştirebiliriz

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

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.