将带符号的 ES6 Class 转换为 JSON
Convert ES6 Class with Symbols to JSON
我已经硬编码 classes 来表示我的 Aurelia 应用程序中的模型。这是一个模型 'PostEdit':
var _postID = Symbol();
var _title = Symbol();
var _text = Symbol();
export class PostEdit {
constructor(postEdit) {
this[_postID] = postEdit.postID;
this.title = postEdit.title;
this.text= postEdit.text;
}
get postID() { return this[_postID]; }
get title() { return this[_title]; }
set title(val) { this[_title] = val; }
get text() { return this[_text]; }
set text(val) { this[_text] = val; }
}
对象被操作后,我需要PUT
和POST
将它返回给服务器。但看起来 Aurelia
的 HttpClient
正在发送一个空的 JSON
字符串 ({}
)。仔细观察,似乎在将 ES6 class 转换为 JSON
时 Symbols
被忽略了。
如何将我所有的属性放入 JSON
字符串中以提交回服务器?
我假设您正在使用符号来保持数据的私密性,但这意味着如果您希望将数据包含在 JSON 表示中,则必须执行一些额外的步骤。
这是一个在您的模型上使用 toJSON
来显式导出您关心的属性的示例
export class PostEdit {
// ...
toJSON() {
return {
postID: this.postID,
title: this.title,
text: this.text
};
}
}
或
export class PostEdit {
// ...
toJSON() {
let {postID, title, text} = this;
return {postID, title, text};
}
}
当在您的实例上调用 JSON.stringify
时,它会自动调用 toJSON
给你的 class 一个 toJSON
方法,returns 一个可字符串化的对象:
export class PostEdit {
constructor(postEdit) {
this[_postID] = postEdit.postID;
this.title = postEdit.title;
this.text = postEdit.text;
}
get postID() { return this[_postID]; }
get title() { return this[_title]; }
set title(val) { this[_title] = val; }
get text() { return this[_text]; }
set text(val) { this[_text] = val; }
toJSON() {
return {
postId: this.postId,
title: this.title,
text: this.text
};
}
}
JSON.stringify
将自动调用它并用结果替换您的实例。
您可能还想在 class 中添加一个 fromJSON
方法,您可以使用它在 JSON.parse
期间恢复实例。在你的情况下这是微不足道的:
static fromJSON(obj) {
return new this(obj);
}
但您可能需要其他 classes 中更复杂的东西。
基于符号的私有变量是 ES6 中封装的重要方法。 JS中的封装很少有道理,但这些是访问器(而不是符号)导致这里出现问题。
访问器在 ES6 类 中是 prototype methods。所以 属性 不是在实例上定义的,而是在原型上定义的,它是不可枚举的。可以在转译代码中看到,或者通过检查
postEditInstance.hasOwnProperty('postID') === false
Object.getPrototypeOf(postEditInstance).hasOwnProperty('postID') === true
Object.getPrototypeOf(postEditInstance).propertyIsEnumerable('postID') === false
JSON.stringify
,另一方面,序列化对象的 only own enumerable properties。
解决方法是使用toJSON方法,根据需要的条件序列化对象。或者在模型的访问器上使用奥卡姆剃刀,尤其是当它们在那里不重要时。
我想使用 Object.assign
将整个 class 分配给一个对象
我最初的观点是在 class 上使用它,将分配的 class 分配给对象。这为 chrome 中的 _json
创建了一个无限循环。坏主意。
class test {
constructor() {
this._json = {type: 'map'};
Object.assign(this, this._json);
}
toJSON(){
Object.assign(this._json, this);
return this._json;
}
}
我不得不排除 _json
变量,所以我迭代 class 变量以排除它。
class test2 {
constructor() {
this._json = {type: 'map'};
Object.assign(this, this._json);
}
toJSON(){
Object.assign(this._json, {
conv() {
let ret = {};
for(let i in this )
{
if(i !== '_json')
ret[i] = this[i];
}
return ret;
}
} );
return this._json;
}
}
但奇怪的是 _json
即使没有 if(i !== '_json')
也会被忽略
尚未完全测试,但我认为这将是一个很好的分享。
要获得更动态的解决方案,请使用:
export class MeMe(){
toJSON() {
return Object.getOwnPropertyNames(this).reduce((a, b) => {
a[b] = this[b];
return a;
}, {});
}
}
或者你可以使用我的 json-decorator :)
import json from "json-decorator";
@json("postID") // pass the property names that you want to ignore
export class MeMe(){
// ...
}
How can I go about getting all my (symbol) properties into a JSON string to submit back to the server?
要获取所有符号属性,您可以使用 Object.getOwnPropertySymbols() 获取直接在给定对象上找到的所有符号属性的数组。如:
const pe = new PostEdit({...});
const symbols = Object.getOwnPropertySymbols(pe);
但是,由于PostEdit的三个符号属性都使用空描述(new Symbol()
,没有任何描述参数),因此无法区分这三个符号。如果上面的symbols
数组被打印出来,你会得到:
//console.log(symbols)
[ Symbol(), Symbol(), Symbol() ]
因此,如果您想 serialize/deserialize PostEdit 实例而不添加 toJSON()
/fromJSON()
方法到 PostEdit
class,您可以提供有意义的描述符号,然后相应地序列化它们:
var _postID = Symbol('postID');
var _title = Symbol('title');
var _text = Symbol('text');
export class PostEdit {
...
}
const pe = new PostEdit({...});
const symbols = Object.getOwnPropertySymbols(pe);
const serializeObj = {};
for (let s of symbols) {
serializeObj[s.description] = pe[s];
}
const serializedText = JSON.stringify(serializeObj);
为了让上面的serialize/deserialize步骤更方便使用,同时解决符号描述相同的问题,我制作了一个名为esserializer的npm模块,并在其pro中实现了这些逻辑版本。
我已经硬编码 classes 来表示我的 Aurelia 应用程序中的模型。这是一个模型 'PostEdit':
var _postID = Symbol();
var _title = Symbol();
var _text = Symbol();
export class PostEdit {
constructor(postEdit) {
this[_postID] = postEdit.postID;
this.title = postEdit.title;
this.text= postEdit.text;
}
get postID() { return this[_postID]; }
get title() { return this[_title]; }
set title(val) { this[_title] = val; }
get text() { return this[_text]; }
set text(val) { this[_text] = val; }
}
对象被操作后,我需要PUT
和POST
将它返回给服务器。但看起来 Aurelia
的 HttpClient
正在发送一个空的 JSON
字符串 ({}
)。仔细观察,似乎在将 ES6 class 转换为 JSON
时 Symbols
被忽略了。
如何将我所有的属性放入 JSON
字符串中以提交回服务器?
我假设您正在使用符号来保持数据的私密性,但这意味着如果您希望将数据包含在 JSON 表示中,则必须执行一些额外的步骤。
这是一个在您的模型上使用 toJSON
来显式导出您关心的属性的示例
export class PostEdit {
// ...
toJSON() {
return {
postID: this.postID,
title: this.title,
text: this.text
};
}
}
或
export class PostEdit {
// ...
toJSON() {
let {postID, title, text} = this;
return {postID, title, text};
}
}
当在您的实例上调用 JSON.stringify
时,它会自动调用 toJSON
给你的 class 一个 toJSON
方法,returns 一个可字符串化的对象:
export class PostEdit {
constructor(postEdit) {
this[_postID] = postEdit.postID;
this.title = postEdit.title;
this.text = postEdit.text;
}
get postID() { return this[_postID]; }
get title() { return this[_title]; }
set title(val) { this[_title] = val; }
get text() { return this[_text]; }
set text(val) { this[_text] = val; }
toJSON() {
return {
postId: this.postId,
title: this.title,
text: this.text
};
}
}
JSON.stringify
将自动调用它并用结果替换您的实例。
您可能还想在 class 中添加一个 fromJSON
方法,您可以使用它在 JSON.parse
期间恢复实例。在你的情况下这是微不足道的:
static fromJSON(obj) {
return new this(obj);
}
但您可能需要其他 classes 中更复杂的东西。
基于符号的私有变量是 ES6 中封装的重要方法。 JS中的封装很少有道理,但这些是访问器(而不是符号)导致这里出现问题。
访问器在 ES6 类 中是 prototype methods。所以 属性 不是在实例上定义的,而是在原型上定义的,它是不可枚举的。可以在转译代码中看到,或者通过检查
postEditInstance.hasOwnProperty('postID') === false
Object.getPrototypeOf(postEditInstance).hasOwnProperty('postID') === true
Object.getPrototypeOf(postEditInstance).propertyIsEnumerable('postID') === false
JSON.stringify
,另一方面,序列化对象的 only own enumerable properties。
解决方法是使用toJSON方法,根据需要的条件序列化对象。或者在模型的访问器上使用奥卡姆剃刀,尤其是当它们在那里不重要时。
我想使用 Object.assign
我最初的观点是在 class 上使用它,将分配的 class 分配给对象。这为 chrome 中的 _json
创建了一个无限循环。坏主意。
class test {
constructor() {
this._json = {type: 'map'};
Object.assign(this, this._json);
}
toJSON(){
Object.assign(this._json, this);
return this._json;
}
}
我不得不排除 _json
变量,所以我迭代 class 变量以排除它。
class test2 {
constructor() {
this._json = {type: 'map'};
Object.assign(this, this._json);
}
toJSON(){
Object.assign(this._json, {
conv() {
let ret = {};
for(let i in this )
{
if(i !== '_json')
ret[i] = this[i];
}
return ret;
}
} );
return this._json;
}
}
但奇怪的是 _json
即使没有 if(i !== '_json')
尚未完全测试,但我认为这将是一个很好的分享。
要获得更动态的解决方案,请使用:
export class MeMe(){
toJSON() {
return Object.getOwnPropertyNames(this).reduce((a, b) => {
a[b] = this[b];
return a;
}, {});
}
}
或者你可以使用我的 json-decorator :)
import json from "json-decorator";
@json("postID") // pass the property names that you want to ignore
export class MeMe(){
// ...
}
How can I go about getting all my (symbol) properties into a JSON string to submit back to the server?
要获取所有符号属性,您可以使用 Object.getOwnPropertySymbols() 获取直接在给定对象上找到的所有符号属性的数组。如:
const pe = new PostEdit({...});
const symbols = Object.getOwnPropertySymbols(pe);
但是,由于PostEdit的三个符号属性都使用空描述(new Symbol()
,没有任何描述参数),因此无法区分这三个符号。如果上面的symbols
数组被打印出来,你会得到:
//console.log(symbols)
[ Symbol(), Symbol(), Symbol() ]
因此,如果您想 serialize/deserialize PostEdit 实例而不添加 toJSON()
/fromJSON()
方法到 PostEdit
class,您可以提供有意义的描述符号,然后相应地序列化它们:
var _postID = Symbol('postID');
var _title = Symbol('title');
var _text = Symbol('text');
export class PostEdit {
...
}
const pe = new PostEdit({...});
const symbols = Object.getOwnPropertySymbols(pe);
const serializeObj = {};
for (let s of symbols) {
serializeObj[s.description] = pe[s];
}
const serializedText = JSON.stringify(serializeObj);
为了让上面的serialize/deserialize步骤更方便使用,同时解决符号描述相同的问题,我制作了一个名为esserializer的npm模块,并在其pro中实现了这些逻辑版本。