使用getter和setter而不是私有变量将Typescript类转换为字符串

时间:2019-01-16 15:37:11

标签: json string typescript class tostring

我有以下打字稿代码:

class Foo {
    private _id: number;
    private _desc: string;

    constructor(id: number, desc: string) {
        this._id = id;
        this._desc = desc;
    }

    public get id(): number {
        return this.id;
    } 

    public set id(value: number) {
        this._id = value;
    }

    public get desc():string {
        return this.desc;
    } 

    public set desc(value: string) {
        this._desc = value;
    }
}

let foo = new Foo(1, 'something');

我想从Typescript类中获取一个字符串,我的意思是使用getter和setter。然后我应该得到以下字符串:

{"id":1,"desc":"something"}

根据此answer,我可以达到以下目的:在类中添加以下方法:

public toJSONString(): string {
    return JSON.stringify(this, Object.keys(this.constructor.prototype));
}

有效。

如果Typescript类包含任何其他子类,则此方法不起作用。

所以,如果我有以下代码:

class Foo {
    private _id: number;
    private _desc: string;
    private _user: Bar;

    constructor(id: number, desc: string, user: Bar) {
        this._id = id;
        this._desc = desc;
        this._user = user;
    }

    public get id(): number {
        return this._id;
    } 

    public set id(value: number) {
        this._id = value;
    }

    public get desc():string {
        return this._desc;
    } 

    public set desc(value: string) {
        this._desc = value;
    }

    public get user(): Bar {
        return this._user;
    } 

    public set user(value: Bar) {
        this._user = value;
    }

    public toJSONString(): string {
        return JSON.stringify(this, Object.keys(this.constructor.prototype));
    }
}

class Bar {
    private _name: string;
    private _surname: string;

    constructor(name: string, surname: string) {
        this._name = name;
        this._surname = surname;
    }

    public get name(): string {
        return this._name;
    } 

    public set name(value: string) {
        this._name = value;
    }

    public get surname():string {
        return this._surname;
    } 

    public set surname(value: string) {
        this._surname = value;
    }
}

let foo = new Foo(1, 'something', new Bar('foo', 'bar'));

如果我使用toJSONString方法,则会得到以下字符串:

{"id":1,"desc":"something","user":{}}

代替此:

{"id":1,"desc":"something","user":{ "name": "foo", "surname": "bar"}}

那么,如何从具有其他子类的Typescript类中获取字符串?

(如果需要,这里是第一个代码的playground,这里是第二个代码的playground

1 个答案:

答案 0 :(得分:1)

这里有两件事要记住:

  1. 定义getter和setter时,它们一旦转换为Javascript便不会成为实例方法,而是使用Object.defineProperty将其添加到原型中。这意味着您不会仅仅使用JSON.stringify

  2. 来获得它们。
  3. 将replacer数组传递给JSON.stringify并告诉它仅使用原型值可以完成此工作,但它对嵌套对象不起作用。事实是JSON.stringify将仅解析具有该名称的属性,无论它位于对象结构中的什么位置。

例如

let a = {
    user: "Foo",
    data: {
        name: "Bar"
    }
};

JSON.stringify(a, ["user", "data"]);

将输出{"user":"Foo","data":{}},因为即使嵌套对象的键是data,对象本身也不具有名为userdata的属性
但是

let a = {
    user: "Foo",
    data: {
        user: "Bar"
    }
};

JSON.stringify(a, ["user", "data"]);

将输出{"user":"Foo","data":{"user":"Bar"}},因为嵌套对象具有一个称为user的属性,就像它的父对象一样

我认为这种行为可能会造成混乱,但是可以通过创建一种方法来实现解决方案,该方法将获取您感兴趣的所有对象的所有属性。我没有在Typescript中找到检查类是否实现的方法一个接口(或扩展一个类),所以即使它不是那么“优雅”,我也必须对我所知道的东西进行一些工作。

abstract class Stringifyiable {
    private isStringifyiable(value): boolean {
        return value != null && (typeof value === 'object' || typeof value === 'function') && value['getJsonKeys'] && typeof value['getJsonKeys'] === 'function';
    }

    public getJsonKeys(): string[] {
        let keys = Object.keys(this.constructor.prototype);
        keys.forEach(key => {
            if (this.isStringifyiable(this[key])) {
                keys = keys.concat(this[key].getJsonKeys());
            }
        });

        return keys;
    }

    public toJSONString(): string {
        return JSON.stringify(this, this.getJsonKeys());
    }
}



class Foo extends Stringifyiable {
    private _id: number;
    private _desc: string;
    private _user: Bar;

    constructor(id: number, desc: string, user: Bar) {
        super();
        this._id = id;
        this._desc = desc;
        this._user = user;
    }

    public get id(): number {
        return this._id;
    } 

    public set id(value: number) {
        this._id = value;
    }

    public get desc():string {
        return this._desc;
    } 

    public set desc(value: string) {
        this._desc = value;
    }

    public get user(): Bar {
        return this._user;
    } 

    public set user(value: Bar) {
        this._user = value;
    }
}



class Bar extends Stringifyiable {
    private _name: string;
    private _surname: string;

    constructor(name: string, surname: string) {
        super();
        this._name = name;
        this._surname = surname;
    }

    public get name(): string {
        return this._name;
    } 

    public set name(value: string) {
        this._name = value;
    }

    public get surname():string {
        return this._surname;
    } 

    public set surname(value: string) {
        this._surname = value;
    }
}

let foo = new Foo(1, 'something', new Bar('foo', 'bar'));
//this will output {"id":1,"desc":"something","user":{"name":"foo","surname":"bar"}}
foo.toJSONString();

在使用循环引用时要小心,因为它会陷入无限循环(不过我敢肯定它可以解决)。