JSON stringify ES6 class property with getter/setter
Solution 1
You can use toJSON
method to customise the way your class serialises to 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
}
}
}
Solution 2
If you want to avoid calling toJson, there is another solution using enumerable and writable:
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;
}
}
Solution 3
As mentioned by @Amadan you can write your own toJSON
method.
Further more, in order to avoid re-updating your method every time you add a property to your class you can use a more generic toJSON
implementation.
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"}
If you want to emit all properties and all fields you can replace const jsonObj = {};
with
const jsonObj = Object.assign({}, this);
Alternatively, if you want to emit all properties and some specific fields you can replace it with
const jsonObj = {
myField: myOtherField
};
Solution 4
I made some adjustments to the script of Alon Bar. Below is a version of the script that works perfectly for me.
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;
}
Thomas Chia
Updated on June 18, 2022Comments
-
Thomas Chia almost 2 years
I have a JavaScript ES6 class that has a property set with
set
and accessed withget
functions. It is also a constructor parameter so the class can be instantiated with said property.class MyClass { constructor(property) { this.property = property } set property(prop) { // Some validation etc. this._property = prop } get property() { return this._property } }
I use
_property
to escape the JS gotcha of using get/set that results in an infinite loop if I set directly toproperty
.Now I need to stringify an instance of MyClass to send it with a HTTP request. The stringified JSON is an object like:
{ //... _property: }
I need the resulting JSON string to preserve
property
so the service I am sending it to can parse it correctly. I also needproperty
to remain in the constructor because I need to construct instances of MyClass from JSON sent by the service (which is sending objects withproperty
not_property
).How do I get around this? Should I just intercept the MyClass instance before sending it to the HTTP request and mutate
_property
toproperty
using regex? This seems ugly, but I will be able to keep my current code.Alternatively I can intercept the JSON being sent to the client from the service and instantiate MyClass with a totally different property name. However this means a different representation of the class either side of the service.