【问题标题】:Convert ES6 Class with Symbols to JSON将带有符号的 ES6 类转换为 JSON
【发布时间】:2016-01-27 17:56:37
【问题描述】:

我有硬编码的类来表示我的 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 类转换为 JSON 时,似乎忽略了 Symbols

如何将我的所有属性放入JSON 字符串以提交回服务器?

【问题讨论】:

  • 您希望它们在 JSON 表示中看起来像什么? JSON 对象属性看起来像 "name": "value",但是应该为 Symbol 使用什么名称?
  • 查看 www.json.org 了解可以用 JSON 表示的值的类型。您可能需要在您的类中提供 toJSON 方法以返回所需的表示形式。
  • @Barmar 最后我需要{ "postID": "1" ...}。我想我希望有一些内置的方法来做到这一点
  • 为什么需要getter和setter?除了使用普通属性时,它们不会做任何事情。
  • @Jonesopolis:绝对是矫枉过正。如果您在 getter/setter 主体中添加验证或解析或其他任何内容,则可以使用它们,但如果您只是分配/访问“更私有”的属性,它几乎没有用处。顺便说一句,符号不是私有的,它们只是被隐藏在正常循环或JSON.stringify 之类的东西中被枚举。任何想要访问的人仍然可以访问它们。

标签: javascript json ecmascript-6 aurelia


【解决方案1】:

我假设您使用符号来保持数据的私密性,但这意味着如果您希望将数据包含在 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

【讨论】:

    【解决方案2】:

    给你的类一个 toJSON 方法,它返回一个可字符串化的对象:

    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 将自动调用它并用结果替换您的实例。

    此外,您可能希望在您的类中添加一个fromJSON 方法,您可以使用该方法在JSON.parse 期间恢复实例。在你的情况下这是微不足道的:

        static fromJSON(obj) {
            return new this(obj);
        }
    

    但您可能需要在其他类中使用更复杂的东西。

    【讨论】:

      【解决方案3】:

      基于符号的私有变量是 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方法,根据需要的条件对对象进行序列化。或者在模型的访问器上使用奥卡姆剃刀,特别是如果它们在那里并不重要。

      【讨论】:

        【解决方案4】:

        我希望使用Object.assign 将整个类分配给一个对象

        我对在将分配的类分配给对象的类上使用它的最初看法。这为 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 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;
            }
        }
        

        但奇怪的是,即使没有if(i !== '_json')_json 也会被忽略

        还没有完全测试过,但我认为这将是一个很好的分享。

        【讨论】:

          【解决方案5】:

          更多动态解决方案使用这个:

          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(){
            // ...
          }
          

          【讨论】:

          • 优雅的解决方案。
          【解决方案6】:

          如何将我的所有(符号)属性转换为 JSON 字符串以提交回服务器?

          要获取所有符号属性,您可以使用Object.getOwnPropertySymbols() 获取直接在给定对象上找到的所有符号属性的数组。如:

          const pe = new PostEdit({...});
          const symbols = Object.getOwnPropertySymbols(pe);
          

          但是,由于 PostEdit 的三个符号属性都使用空描述(new Symbol(),没有任何描述参数),因此无法区分这三个符号。如果上面的symbols数组打印出来,你会得到:

          //console.log(symbols)
          [ Symbol(), Symbol(), Symbol() ]
          

          因此,如果您想序列化/反序列化 PostEdit 实例而不向 PostEdit 类添加 toJSON()/fromJSON() 方法,您可以为符号提供有意义的描述,然后相应地序列化它们:

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

          为了使上述序列化/反序列化步骤更方便使用,并修复相同符号描述的问题,我制作了一个名为 esserializer 的 npm 模块,并在其专业版中实现了这些逻辑。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-04-20
            • 1970-01-01
            • 2020-05-27
            • 1970-01-01
            • 2018-01-03
            相关资源
            最近更新 更多