HTTP:从 JSON 对象到 Typescript 的类型断言

Posted

技术标签:

【中文标题】HTTP:从 JSON 对象到 Typescript 的类型断言【英文标题】:HTTP: Type assertion from JSON object to Typescript 【发布时间】:2016-10-14 00:52:11 【问题描述】:

我正在阅读接口和类型断言。一直在看类似的页面:

Typescript parse json with class and interface How do I cast a json object to a typescript class(这个跟TS没关系!) Parse complex json objects with Typescript TS doc about interfaces

我已经掌握了窍门,而且理解基础知识非常简单。虽然:当涉及 HTTP 时,我无法找到如何将 JSON 对象键入到我在应用程序中使用的对象。

例如,我有一个教师对象数组。每个教师都有一个 id、名字和一个学生数组。我有另一个班级学生,其中包含他们的属性......等等。

在我读到的那些链接的某个地方,除非您需要对对象执行操作,否则只有一个接口就足够了。但是除非您想对 Object 执行操作,否则您需要一个单独的类?

我实际的教师课程从...开始:

export class Teacher 

    private students: Array<Student>;

    constructor(public id: string, public name: string) 
        this.students = new Array<Student>();
    

    public getStudents(): Array<Student> 
        return this.students;
    


首先,如果我想将 JS 对象强制转换(或断言类型)为 Teacher 对象,代码会是什么样子?

现在我的代码看起来像这样:

服务:

getTeachers() 
    return this.http.get('someUrl')
    .map((res: Response) => res.json())

组件(Angular 2 组件):

export class ListComponent implements OnActivate 

id: string;
name: string;
teachers: Teacher[];

constructor(public _service: Service, public _router: Router) 



routerOnActivate(): void 
    this._service.getTeachers()
        .subscribe(teachers => this.teachers = teachers);

我的界面如下所示:

export interface TeacherJSON 
        id: string,
        name: string,
        students: Array<Student>;

如果上面的接口还不足以对对象执行动作,我该如何前进?我确实了解到您可以向界面添加方法,例如:

interface Thing 
    a: number;
    b: string;
    foo(s: string, n: number): string;


process(x: Thing) 
    return x.foo("abc");

我确实理解上面的例子,但我想 http、映射和订阅让我失望,我不知道如何在我的代码中实现它!

【问题讨论】:

【参考方案1】:

那么显然我需要先将组件中的教师数组更改为我的界面实例?

没有接口实例这样的东西。接口是抽象的,它们只存在于 TypeScript 中,它们甚至不编译为 javascript。您编写它们是为了在您的编辑器中获得代码完成和其他好东西;在大型应用程序中为您提供帮助,因此您不必记住每个对象都有哪些属性和方法。

当您必须实例化一个对象时,接口不能这样做 - 您将使用一个类,并像往常一样创建属性和方法。在您的示例中执行此操作的正确方法是:

interface ITeacher
  id: string,
  name: string,
  students: Array<Student>;
;
class Teacher implements ITeacher 
  id: string,
  name: string,
  students: Array<Student>;

在您的情况下,不需要实现接口,TypeScript 足够聪明,也可以从类中提取信息,所以它们是相同的:

teachers: Teacher[];
teachers: ITeacher[];

如果您有多种类型的教师,并且您想确保每个教师都具有所有必要的属性/方法,那么创建一个界面会很有意义:

class FirstGradeTeacher implements ITeacher
class SecondGradeTeacher implements ITeacher

您可能已经注意到我根本没有提到 JSON。 忘掉它! ...在使用您的对象模型(类或接口)时。它只是您的逻辑模型的数据格式。当您构建模型并计划它们应该如何工作时,您不需要关心协议和格式(Http 服务会处理这些)。

getTeachers(): Observable<Teacher> 
  return this.http.get('someUrl')
    .map((res: Response) => res.json())

这段代码是你如何使用界面的。您只需告诉 TypeScipt,当您收到来自 someUrl 的响应并将其解析为 json 时,您希望数据是教师类型。

然后在其他地方当你subscribe() 到它时,它可以告诉你你得到的对象有idnamestudents 属性:

this._service.getTeachers()
  .subscribe((teachers: Teacher[]) => this.teachers = teachers);

希望这会有所帮助(:

【讨论】:

感谢您的回答。当然很难看出我的问题,因为你看不到完整的代码。我不能用我的界面扩展我的教师类,因为学生在课堂上是私人的。有人查看了我的代码,并被告知要实现一个接口来解决我的问题。我正在丢失数据,例如当我检索一位教师时。在那里,我必须将传入的老师映射为“新”老师,否则我会收到 self.context 错误。如果你看我的老师课,我只能说new Teacher(teacher.id, teacher.name)。所以我已经在那里丢失了数据。 在我的服务中添加了 getTeacher 方法来展示我丢失数据的问题之一。 @Sasxa 为什么 res.json() 会转换为 Teacher?我正在寻找一些解释这一点的文档。 它没有转换它,它仍然只是一个 JSON。但是因为您希望响应将具有与教师相同的结构,所以您将其进行类型转换,并且您的工具和编辑器可以帮助您进行类型检查、自动完成、调试等...@TomDeseyn @Sasxa 我没有找到演员表操作。 map 调用返回 Observable,它在哪里转换为 Observable【参考方案2】:

如果我很好地理解了您的问题,您需要为以Object 作为原型的反序列化 JSON 对象提供一些方法。目前,纯 javascript API 无法做到这一点,因为您从 REST 服务接收到的内容是纯字符串,通过 JSON.parse 成为对象。您应该使用另一种反序列化机制,该机制能够在运行时检查您的类,并从反序列化的 JSON 重新构建它们的实例。通过这种方式,您将拥有新鲜的 JSON 对象,但具有正确的原型,该原型具有该类的所有方法。我试着举个例子。

假设您正在调用一个 REST 端点,该端点为您提供Teacher-like 对象。在某些时候你会做var freshObject = JSON.parse(receivedString)(实际上Angular已经做到了,但我们会改进这个机制);最简单的事情(但不是最明智的事情)是将freshObject 的原型更改为Teacher 原型:Object.setPrototypeof(freshObject, Teacher.prototype)。现在您可以freshObject 上调用Teacher 方法,但这显然是一个肤浅的变化,因为嵌套对象(在本例中为Students)仍然以Object 作为其原型,并且问题又来了。

现在很明显,我们需要能够递归研究Teacher类结构的东西,同时使用正确的原型重新构建接收到的对象(实际上,您可以重新构建新的原型,而不是更改原型)副本,应该不那么重)。你可能想要这样的东西:Teacher.getClass().members,它会给你这些信息:

students 字段的名称 students 字段的类型(在本例中为Array&lt;Students&gt;) 组件类Student的构造函数

如果您有此类信息,您可以递归地执行我们在上一个示例中所做的相同操作。

我最近发布了一个增强版的 TypeScript 编译器,它允许这个用例,我为你准备了一个完整的工作示例here,here 你可以找到编译器项目。如果这能解决您的问题,请告诉我。

【讨论】:

【参考方案3】:

假设您通过 http 获取教师列表,您可以对其进行过滤以获取您需要的:

getById(id: string) 
    return this.http.get('someUrl' + id)
        .map((res: Response) => res.json())
        .filter(teacher => teacher.id === id));

【讨论】:

以上是关于HTTP:从 JSON 对象到 Typescript 的类型断言的主要内容,如果未能解决你的问题,请参考以下文章

ReactJS - 从 URL 获取 json 对象数据

从 json 数组快速加载到对象数组中

如何从 Angular2(Typescript)中的 Json 数组中获取值的总和

如何将 Json 对象从 Fiddler 传递到 Webapi2

C#从http上拿返回JSON数据

如何使用 doGet 方法捕获从 android 应用程序发送到 servlet 的 JSON 对象?