将带符号的 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; }

}

对象被操作后,我需要PUTPOST将它返回给服务器。但看起来 AureliaHttpClient 正在发送一个空的 JSON 字符串 ({})。仔细观察,似乎在将 ES6 class 转换为 JSONSymbols 被忽略了。

如何将我所有的属性放入 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中实现了这些逻辑版本。