2

我使用 NSwag 为 swagger API 端点生成 TypeScript 类型和类。结果类包含.toJSON()每个对象的方法,当使用JSON.stringify().

序列化单个对象时一切正常,但是当我尝试序列化对象数组时会抛出一个奇怪的错误:

angular.js:14199 TypeError: Cannot create property 'code' on string '0'
    at Dashboard.toJSON (App/models/api.js:785:34)
    at JSON.stringify (<anonymous>)

触发它的代码非常简单:

console.log(JSON.stringify([
        Dashboard.fromJS({
            code: "1212312",
            name: "tresads",
            description: "some description"
        }),
        Dashboard.fromJS({
            code: "1212312",
            name: "tresads",
            description: "some description"
        })
    ]));

课程摘录:

export class Dashboard implements IDashboard {
    code?: string | undefined;
    ...

    constructor(data?: IDashboard) {
        if (data) {
            for (var property in data) {
                if (data.hasOwnProperty(property))
                    (<any>this)[property] = (<any>data)[property];
            }
        }
    }

    init(data?: any) {
        if (data) {
            this.code = data["code"];
            ...
        }
    }

    static fromJS(data: any): Dashboard {
        let result = new Dashboard();
        result.init(data);
        return result;
    }

    toJSON(data?: any) {
        data = data ? data : {};
        data["code"] = this.code;
        ...
        return data; 
    }

    clone() {
        const json = this.toJSON();
        let result = new Dashboard();
        result.init(json);
        return result;
    }
}

知道为什么用“0”参数JSON.stringify()调用该方法吗?toJSON()

4

1 回答 1

3

The method toJSON will be called with one argument, which is the property name to which this is assigned. In essence, the value of your interest is not that argument, but this, which will be bound to the value that you could transform. Since you call stringify with an array, toJSON will get called with the enumerable properties of that array, i.e. 0 and 1, while this will be the corresponding Dashboard object.

Also, I have the impression you could make good use of Object.assign which will copy properties from one object to another, which is essentially what you do in the constructor's for loop.

So here is how you could do it. I removed the typescript decoration and used plain JavaScript, but the principle remains the same:

class Dashboard {
    constructor(data) {
        // Object.assign does essentially what you want with the loop:
        Object.assign(this, data); 
    }

    init(data) {
        return Object.assign(this, data); 
    }

    static fromJS(data) {
        return new Dashboard(data);
    }

    toJSON(key) { 
        // `key` is the key/index of the property in the parent object.
        //    That probably is of no interest to you. You need `this`.
        // Extract properties into plain object, and return it for stringification
        return Object.assign({}, this);
    }

    clone() {
        return new Dashboard(this);
    }
}

console.log(JSON.stringify([
        Dashboard.fromJS({
            code: "1212312",
            name: "tresads",
            description: "some description"
        }),
        Dashboard.fromJS({
            code: "1212312",
            name: "tresads",
            description: "some description"
        })
    ]));

Actually, in the given example, you don't need toJSON at all, since the properties of the Dashboard instance are enumerable, and so they will be stringified anyway. If for some reason you do need it for a certain transformation to happen, then of course you still need to include the logic for that transformation, since Object.assign is just a simple copy.

于 2017-06-16T12:30:00.473 回答