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

Posted

技术标签:

【中文标题】将 JSON.stringify 与 TypeScript getter/setter 结合使用【英文标题】:Using JSON.stringify in conjunction with 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# 的属性命名约定 【参考方案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) ***.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://***.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())

【讨论】:

以上是关于将 JSON.stringify 与 TypeScript getter/setter 结合使用的主要内容,如果未能解决你的问题,请参考以下文章

关于JSON.stringify()与JSON.parse()

JSON.stringify与jQuery.parseJSON

json.stringify()与json.parse()

JSON.stringify() 与JSON.parse()

JSON.parse()与JSON.stringify()

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