web-dev-qa-db-ja.com

JSONがゲッター/セッターでES6クラスプロパティを文字列化

setでプロパティが設定され、get関数でアクセスできるJavaScript ES6クラスがあります。これはコンストラクターパラメーターでもあるため、クラスは上記のプロパティでインスタンス化できます。

_class MyClass {
  constructor(property) {
    this.property = property
  }

  set property(prop) {
  // Some validation etc.
  this._property = prop
  }

  get property() {
    return this._property
  }
}
_

__property_を使用して、propertyに直接設定すると、無限ループが発生するget/setを使用するというJSの問題を回避します。

次に、MyClassのインスタンスを文字列化して、HTTPリクエストで送信する必要があります。文字列化されたJSONは次のようなオブジェクトです。

_{
   //...
   _property:
}
_

結果のJSON文字列がpropertyを保持して、送信先のサービスが正しく解析できるようにする必要があります。サービスから送信されたJSONからMyClassのインスタンスを作成する必要があるため、propertyもコンストラクターに残す必要があります(__property_ではなくpropertyでオブジェクトを送信しています)。

どうすればこれを回避できますか? MyClassインスタンスをインターセプトしてからHTTPリクエストに送信し、正規表現を使用して__property_をpropertyに変更する必要がありますか?これは見苦しいようですが、現在のコードを保持できます。

あるいは、サービスからクライアントに送信されているJSONをインターセプトして、まったく異なるプロパティ名でMyClassをインスタンス化することもできます。ただし、これは、サービスのいずれかの側のクラスの異なる表現を意味します。

25
Thomas Chia

toJSON method を使用して、クラスをJSONにシリアル化する方法をカスタマイズできます。

class MyClass {
  constructor(property) {
    this.property = property
  }

  set property(prop) {
  // Some validation etc.
  this._property = prop
  }

  get property() {
    return this._property
  }

  toJSON() {
    return {
      property: this.property
    }
  }
}
33
Amadan

ToJsonの呼び出しを避けたい場合は、列挙可能および書き込み可能を使用する別の解決策があります。

class MyClass {

  constructor(property) {

    Object.defineProperties(this, {
        _property: {writable: true, enumerable: false},
        property: {
            get: function () { return this._property; },
            set: function (property) { this._property = property; },
            enumerable: true
        }
    });

    this.property = property;
  }

}
14
Richard Časár

@Amadanで述べたように、独自の toJSON メソッドを作成できます。

さらに、クラスにプロパティを追加するたびにメソッドが更新されないようにするために、より一般的なtoJSON実装を使用できます。

class MyClass {

  get prop1() {
    return 'hello';
  }
  
  get prop2() {
    return 'world';
  }

  toJSON() {

    // start with an empty object (see other alternatives below) 
    const jsonObj = {};

    // add all properties
    const proto = Object.getPrototypeOf(this);
    for (const key of Object.getOwnPropertyNames(proto)) {      
      const desc = Object.getOwnPropertyDescriptor(proto, key);
      const hasGetter = desc && typeof desc.get === 'function';
      if (hasGetter) {
        jsonObj[key] = desc.get();
      }
    }

    return jsonObj;
  }
}

const instance = new MyClass();
const json = JSON.stringify(instance);
console.log(json); // outputs: {"prop1":"hello","prop2":"world"}

すべてのプロパティおよびすべてのフィールドを出力する場合は、const jsonObj = {};

const jsonObj = Object.assign({}, this);

または、すべてのプロパティと特定のフィールドを出力する場合は、

const jsonObj = {
    myField: myOtherField
};
9
Alon Bar

Alon Barのスクリプトを調整しました。以下は、私にとって完璧に機能するスクリプトのバージョンです。

toJSON() {
        const jsonObj = Object.assign({}, this);
        const proto = Object.getPrototypeOf(this);
        for (const key of Object.getOwnPropertyNames(proto)) {
            const desc = Object.getOwnPropertyDescriptor(proto, key);
            const hasGetter = desc && typeof desc.get === 'function';
            if (hasGetter) {
                jsonObj[key] = this[key];
            }
        }
        return jsonObj;
    }
7
bits

内部使用にはprivate fieldsを使用します。

class PrivateClassFieldTest {
    #property;
    constructor(value) {
        this.property = value;
    }
    get property() {
        return this.#property;
    }
    set property(value) {
        this.#property = value;
    }
}

class Test {
        constructor(value) {
                this.property = value;
        }
        get property() {
                return this._property;
        }
        set property(value) {
                this._property = value;
        }
}

class PublicClassFieldTest {
        _property;
        constructor(value) {
                this.property = value;
        }
        get property() {
                return this.property;
        }
        set property(value) {
                this._property = value;
        }
}

class PrivateClassFieldTest {
        #property;
        constructor(value) {
                this.property = value;
        }
        get property() {
                return this.#property;
        }
        set property(value) {
                this.#property = value;
        }
}

console.log(JSON.stringify(new Test("test")));
console.log(JSON.stringify(new PublicClassFieldTest("test")));
console.log(JSON.stringify(new PrivateClassFieldTest("test")));