web-dev-qa-db-ja.com

JavaScriptでJSON文字列を特定のオブジェクトプロトタイプに解析する

JSON文字列を解析してJavaScriptオブジェクトに変換する方法を知っています。 JSON.parse()は、最新のブラウザー(およびIE9 +)で使用できます。

それは素晴らしいことですが、そのJavaScriptオブジェクトを特定のJavaScriptオブジェクト(つまり、特定のプロトタイプ)に変換するにはどうすればよいですか?

たとえば、次のものがあるとします。

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文字列を汎用JavaScriptオブジェクトに変換する方法を疑問に思っていません。 JSON文字列を「Foo」オブジェクトに変換する方法を知りたいです。つまり、オブジェクトには「テスト」機能と「a」および「b」プロパティが必要です。

UPDATEいくつかの研究を行った後、私はこれを考えました...

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

それは機能しますか?

UPDATE 2017年5月:これを行う「モダンな」方法は、 Object.assign を経由しますが、この関数はIE 11以前のAndroidブラウザー。

150
BMiner

現在の回答には、多くの手巻きまたはライブラリコードが含まれています。これは必要ありません。

  1. JSON.parse('{"a":1}')を使用してプレーンオブジェクトを作成します。

  2. 標準化された関数のいずれかを使用して、プロトタイプを設定します。

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

以下の例を参照してください(この例ではネイティブJSONオブジェクトを使用します)。私の変更は大文字でコメントされています:

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
68
Oliver Moran

JSONのシリアル化/逆シリアル化機能を追加しますか?次に、これを見てください:

これを達成したい:

UML

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.titlethis.authorなどのすべてのプロパティ定義をvar titlevar authorなどで変更し、ゲッターを追加してUML定義を実現できます。

38
Gabriel Llamas

私が役に立つと思うブログ投稿: nderstanding JavaScript Prototypes

オブジェクトの__proto__プロパティを台無しにすることができます。

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

これにより、fooJSONはFooプロトタイプを継承できます。

しかし、これはIEで機能するとは思わない...少なくとも私が読んだことから。

18
BMiner

質問に何か欠けているのですか、それとも他の人が reviverNAME _ パラメータの JSON.parse 2011年以降言及していないのですか?

動作するソリューションの単純なコードは次のとおりです: 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

PS:あなたの質問は混乱しています:>>それは素晴らしいことですが、どうすればそのJavaScriptオブジェクトを特定のJavaScriptオブジェクト(つまり特定のプロトタイプ)に変換できますか?JSON解析について尋ねるタイトルと矛盾しますが、引用された段落はJSランタイムオブジェクトプロトタイプの置換について尋ねます。

7
Philipp Munin

別のアプローチとして Object.create を使用することもできます。最初の引数としてプロトタイプを渡し、2番目の引数としてプロパティ名のマップを記述子に渡します。

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

json-dry というパッケージを作成しました。 (循環)参照とクラスインスタンスもサポートします。

クラスに2つの新しいメソッド(プロトタイプのtoDryと静的メソッドとしてのunDry)を定義し、クラス(Dry.registerClass)を登録する必要があります。

2
skerit

完全を期すために、ここで簡単なワンライナーを作成しました(非Fooプロパティをチェックする必要はありませんでした)。

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
Rob

オプションの引数をコンストラクタに追加して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));
        }
    }
}
1
Jason Goemaat

これは技術的には望んでいることではありませんが、処理するオブジェクトのタイプを事前に知っている場合、既知のオブジェクトのプロトタイプの呼び出し/適用メソッドを使用できます。

これを変更できます

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

これに

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

見つけることができたソリューションを組み合わせて、カスタムオブジェクトとそのすべてのフィールドを自動的に解析できる汎用的なものにコンパイルし、逆シリアル化後にプロトタイプメソッドを使用できるようにしました。

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
vir us