将 JSON.stringify 与 TypeScript getter/setter 结合使用

Using JSON.stringify in conjunction with TypeScript getter/setter

我在 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 展示了当前的行为。

不,您不能让 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}

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

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

基于@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方法

我写了一个小库 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);
    }
}

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

我们在此处添加 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;
}

不是动态的,但可以工作

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