【问题标题】:How to transform JSON objects into TS class如何将 JSON 对象转换为 TS 类
【发布时间】:2017-12-27 05:00:51
【问题描述】:

我正在尝试将 Django 响应转换为 Angulars User 数组。有几个原因,例如不同的变量名称(first_name 与 firstName)以及 Angular User 构造函数内部有一些逻辑。

简单地说 Django User => Angular User

服务器响应示例:

[
  {"id":2,"email":"test1@test.com","first_name":"Name 1","client":{"id":1}}},
  {"id":3,"email":"test2@test.com","first_name":"Name 2","client":{"id":2}}}
]

我想要的是转换成这种格式:

export class User {
    // contructor that transforms
    id: number;
    email: string;
    firstName: string;
    isClient: boolean = false;
}

我目前已经“解决”了这个问题,但我想知道是否有更好的解决方案。

内部something.service.ts

public getClients(): Observable<User[]> {
    return this.http.get(this.getClientsUrl)
    .map(r => r.json() || []).map(x => x.map(y => new User(y)));
}

当我说更好的解决方案时,虽然这可行,但它看起来不太可读。如果你这样做一次不是问题,但是当你有很多请求时,你会开始考虑一个更好的解决方案(一种处理这个问题的方法?)。有什么更好的方法?有点像拥有

return this.http.get(this.getClientsUrl)
.map(transformResponse);

【问题讨论】:

  • 有什么理由要通过类而不是接口来定义它?

标签: angular typescript


【解决方案1】:

我找到的一个解决方案(在 Roman C 的一些帮助下)是扩展 Service 类。此解决方案适用于 Array 和 Object 响应。

export class ServiceUtils {
    protected transformResponse(response: Response, myClass: any) {
        let body = response.json();

        if (body.constructor === Array) {
            return body.map(element => new myClass(element)) || []
        } 

        return new myClass(body) || new myClass();
    }
}

使用如下:

export class SomeService extends ServiceUtils {

    constructor(private http: Http) {
        super(); // Unfortunate side effect
     }

    public getClients(): Observable<User[]> {
        return this.http.get(this.getClientsUrl)
        .map(response => this.transformResponse(response, User));
    }
}

编辑:

一个更好的解决方案(不扩展Service):

import { Response } from '@angular/http';


export class ServiceUtils {
    public static transformResponse(response: Response, myClass: any) {
        let body = response.json();

        if (body.constructor === Array) {
            return body.map(element => new myClass(element)) || []
        } 

        return new myClass(body) || new myClass();
    }
}

并使用它:

public getClients(): Observable<User[]> {
    return this.http.get(this.getClientsUrl)
    .map(r => ServiceUtils.transformResponse(r, User));
}

【讨论】:

    【解决方案2】:

    如果您以不同的方式格式化您的代码并使用更明确定义的名称,它会更易读,例如

    public getClients(): Observable<User[]> {
        return this.http.get(this.getClientsUrl)
            .map(response => response.json() || [])
            .map(users => users.map(user => new User(user)));
    }
    

    我创建了一个codepen example,它会注销格式正确的服务器响应。

    从另一个答案中读取你的一个 cmets:

    类似这样的东西,但我希望 transformResponse 只实现一次,这意味着它也应该接受类作为参数。

    我认为你想要TypeScript generics,它可能看起来像这样:

    public getClients<T>(type: T): Observable<T[]> {
        return this.http.get(this.getClientsUrl)
            .map(response => response.json() || [])
            .map(clients => clients.map(client => new type(user)));
    }
    

    如果你想结合 Roman C 的方法:

    public getClients(): Observable<User[]> {
        return this.http.get(this.getClientsUrl)
            .map(response => response.json() || [])
            .map(users => users.map(user => this.mapServerResponse<User>(user, User)));
    }
    
    mapServerResponse<T>(response, type: T): T {
        return new type(response);
    }
    

    【讨论】:

    • 我简化了大部分代码,以便在 StackOverflow 上更容易阅读。但我确实喜欢您的第一个解决方案,与我提出的解决方案不同,它没有缺点且易于理解。
    • 尝试您的第一个解决方案时,我收到Error: Error trying to diff '[object Object]'. Only arrays and iterables are allowed。
    • 您能在 codepen 示例中复制它吗?或者提供更多信息,例如服务器响应肯定是您在答案中显示的格式吗?从this question 看来,它可能没有从 getClients() 返回一个数组,你能注销结果看看它给你什么吗?另一种可能性可能是您是否记得订阅 observable 而不仅仅是直接使用 observable?
    • 很难找到一个可行的例子。但是问题是 response.json() 返回一个用户数组,而不是单个用户,所以你需要使用 .map(response =&gt; response.json() || []).map(users =&gt; users.map(user =&gt; new User(user))); 这几乎是我在问题中的解决方案。
    【解决方案3】:

    一个TS界面是这样的:

    interface SquareConfig {
        color: string;
        width?: number;
        enabled: boolean;
    }
    

    您想将给定对象转换为 TS 接口(真的),还是想让响应对象更具可读性?这完全不同。
    因为上面例子中的“我要什么”不是TS接口。

    【讨论】:

    • 我改了一下标题
    • 我认为它涉及到用例:一个类可以有更多的功能(方法),其中一个接口只定义一组属性
    【解决方案4】:

    应该是这样的

    return this.http.get(this.getClientsUrl)
    .map(r => this.transformResponse(r));
    

    【讨论】:

    • 类似这样的东西,但我希望 transformResponse 只实现一次,这意味着它也应该接受类作为参数。
    • 它不应该接受一个类,但它应该接受响应对象。
    猜你喜欢
    • 2019-01-26
    • 1970-01-01
    • 2014-10-10
    • 1970-01-01
    • 2014-05-17
    相关资源
    最近更新 更多