【问题标题】:Using JSON.stringify in conjunction with TypeScript getter/setter将 JSON.stringify 与 TypeScript getter/setter 结合使用
【发布时间】:2017-02-26 02:07:00
【问题描述】:

我在 TypeScript 中使用 getter/setter 访问器。由于变量和方法不可能有相同的名称,因此我开始在变量前面加上一个低破折号,就像在许多示例中所做的那样:

private _major: number;

get major(): number {
  return this._major;
}
set major(major: number) {
  this._major = major;
}

现在当我使用 JSON.stringify() 方法将对象转换为 JSON 字符串时,它将使用变量名作为键:_major。

由于我不希望 JSON 文件的所有键都以低破折号为前缀,因此是否有可能使 TypeScript 使用 getter 方法的名称(如果可用)?或者有没有其他方法可以使用 getter/setter 方法但仍能产生干净的 JSON 输出?

我知道有一些方法可以在将 JSON 键写入字符串输出之前手动修改它们。我很好奇是否有更简单的解决方案。

Here is a JSFiddle 演示当前行为。

【问题讨论】:

  • 我假设另一种方法是使用属性的大写,例如get Major()... 类似于 C# 的属性命名约定

标签: json typescript getter-setter


【解决方案1】:

不,您不能让 JSON.stringify 使用 getter/setter 名称而不是属性名称。

但你可以这样做:

class Version {
    private _major: number;

    get major(): number {
        return this._major;
    }

    set major(major: number) {
        this._major = major;
    }

    toJsonString(): string {
        let json = JSON.stringify(this);
        Object.keys(this).filter(key => key[0] === "_").forEach(key => {
            json = json.replace(key, key.substring(1));
        });

        return json;
    }
}

let version = new Version();
version.major = 2;
console.log(version.toJsonString()); // {"major":2}

【讨论】:

  • 参考以下答案,您可以在对象上定义一个 toJSON 函数,该函数会自动被 JSON.stringify(yourclassobject) stackoverflow.com/questions/29705211/… 使用
  • @N15M0_jk 是的,确实如此,但我不想“覆盖”JSON.stringify 的行为,而只是使用 version.toJsonString()
  • 这会产生错误:TypeError: myObject.toJsonString is not a function
  • @TeodorKolev 我刚刚在打字稿操场上检查了这个确切的代码,效果很好。
  • 使用toJSON。出于某种原因,没有人绝对应该使用toJSONvalueOf。我想没有多少人知道这些存在。例如,class User {...} 可以利用方法valueOf() { return this.id; },因此它可以用作var id = +user;。不确定这是否为您提供了更多的筹码。
【解决方案2】:

我认为遍历属性和字符串操作是危险的。我会使用对象本身的原型,如下所示:

public static toJSONString() : string {
    return JSON.stringify(this, Object.keys(this.constructor.prototype)); // this is version class
}

【讨论】:

  • 这是有效的,但它只返回 setter 和 getter。如何获取不是 getter 和 setter 的所有其他属性?
【解决方案3】:

基于@Jan-Aagaard 解决方案我已经测试了这个

public toJSON(): string {
    let obj = Object.assign(this);
    let keys = Object.keys(this.constructor.prototype);
    obj.toJSON = undefined;
    return JSON.stringify(obj, keys);
}

为了使用 toJSON 方法

【讨论】:

  • 为了得到完整的对象,我用 let keys = Object.keys(this).concat(Object.keys(this.constructor.prototype)) 代替
  • 这是正确的答案。但我认为您不需要 obj.toJSON = undefined; 行,因为 JSON.stringify 完全忽略了方法。如果我不正确,请告诉我。
  • @Cody 是的,它忽略了序列化方法,但是如果toJSON 方法仍然定义,它将调用它而不是直接序列化对象,这会产生一个递归的永恒循环。您需要“取消定义”toJSON 以避免这种情况。
【解决方案4】:

我编写了一个小型库 ts-typed,它为运行时类型生成 getter/setter。我在使用 JSON.stringify() 时遇到了同样的问题。所以我通过添加一种序列化程序来解决它,并建议实现一种 toString(在 Java 中)购买调用它 toJSON。

这是一个例子:

import { TypedSerializer } from 'ts-typed';

export class RuntimeTypedClass {
    private _major: number;

    get major(): number {
       return this._major;
    }

    set major(major: number) {
       this._major = major;
    }
    /**
    * toString equivalent, allows you to remove the _ prefix from props.
    *
    */
    toJSON(): RuntimeTypedClass {
        return TypedSerializer.serialize(this);
    }
}

【讨论】:

    【解决方案5】:

    旧问题的新答案。对于 getter/setter 没有私有字段,或者私有字段名称与 getter/setter 不同的情况,我们可以使用 Object.getOwnPropertyDescriptors 从原型中找到 get 方法。

    https://stackoverflow.com/a/60400835/2325676

    我们在这里添加了toJSON 函数,以便它与其他海报提到的JSON.stringify 一起使用。这意味着我们不能在toJSON 中调用JSON.stringify(),因为它会导致无限循环,所以我们使用Object.assign(...) 进行克隆

    我还删除了 _private 字段作为整理措施。您可能希望删除不想包含在 JSON 中的其他字段。

    public toJSON(): any {
    
        //Shallow clone
        let clone: any = Object.assign({}, this); 
    
        //Find the getter method descriptors
        //Get methods are on the prototype, not the instance
        const descriptors = Object.getOwnPropertyDescriptors(Object.getPrototypeOf(this))
    
        //Check to see if each descriptior is a get method
        Object.keys(descriptors).forEach(key => {
            if (descriptors[key] && descriptors[key].get) {
    
                //Copy the result of each getter method onto the clone as a field
                delete clone[key];
                clone[key] = this[key]; //Call the getter
            }
        });
    
        //Remove any left over private fields starting with '_'
        Object.keys(clone).forEach(key => {
            if (key.indexOf('_') == 0) {
                delete clone[key];
            }
        });
    
        //toJSON requires that we return an object
        return clone;
    }
    

    【讨论】:

      【解决方案6】:

      不是动态的,但它可以工作

      export class MyClass{
           text: string
      
           get html() {
               return this.text.toString().split("\n").map(e => `<p>${e}</p>`).join('');
           }
      
           toJson(): string {
               return JSON.stringify({ ...this, html: this.html })
           }
      }
      

      通话中

      console.log(myClassObject.toJson())
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-11-03
        • 1970-01-01
        • 2018-04-12
        • 1970-01-01
        • 1970-01-01
        • 2023-03-29
        • 2015-05-04
        相关资源
        最近更新 更多