我从AJAX调用REST服务器接收JSON对象 . 此对象具有与我的TypeScript类匹配的属性名称(这是this question的后续内容) .
初始化它的最佳方法是什么?我认为this不会起作用,因为类(&JSON对象)具有作为对象列表的成员和作为类的成员,并且这些类具有作为列表和/或类的成员 .
但我更喜欢一种查找成员名称并将其分配的方法,根据需要创建列表并实例化类,因此我不必为每个类中的每个成员编写显式代码(有很多!)
我从AJAX调用REST服务器接收JSON对象 . 此对象具有与我的TypeScript类匹配的属性名称(这是this question的后续内容) .
初始化它的最佳方法是什么?我认为this不会起作用,因为类(&JSON对象)具有作为对象列表的成员和作为类的成员,并且这些类具有作为列表和/或类的成员 .
但我更喜欢一种查找成员名称并将其分配的方法,根据需要创建列表并实例化类,因此我不必为每个类中的每个成员编写显式代码(有很多!)
13 回答
你现在可以使用
Object.assign
我当前正在使用Typescript 2.0.2,这似乎是一个ES6功能 .这是
HalJson
这就是chrome说的
所以你可以看到它没有递归地分配
你可以在下面做
和
这些是一些快速拍摄,以显示几种不同的方式 . 它们绝不是“完整的”,作为免责声明,我不认为这样做是个好主意 . 此外,代码不是太干净,因为我只是很快地将它们拼凑在一起 .
另外作为注释:当然,反序列化的类需要有默认的构造函数,就像我知道任何类型的反序列化的所有其他语言一样 . 当然,如果你调用一个没有参数的非默认构造函数,Javascript就不会抱怨,但是那个类更好地为它做好准备(而且,它实际上不是“typescripty方式”) .
选项#1:根本没有运行时信息
这种方法的问题主要是任何成员的名称必须与其类匹配 . 这会自动将您限制为每个 class 相同类型的一名成员,并违反了一些良好做法规则 . 我强烈反对这一点,但只是在这里列出,因为这是我写这个答案时的第一个“草稿”(这也是为什么名字是“Foo”等) .
选项#2:名称属性
为了摆脱选项#1中的问题,我们需要获得JSON对象中节点类型的某种信息 . 问题是在Typescript中,这些东西是编译时构造,我们在运行时需要它们 - 但运行时对象在设置之前根本不知道它们的属性 .
一种方法是让类知道他们的名字 . 但是,您也需要在JSON中使用此属性 . 实际上,你只需要在json中:
选项#3:明确说明成员类型
如上所述,类成员的类型信息在运行时不可用 - 除非我们使其可用 . 我们只需要为非原始成员执行此操作,我们很乐意去:
选项#4:冗长而又整洁的方式
2016年1月3日更新:正如@GameAlchemist在评论中指出的那样,从Typescript 1.7开始,下面描述的解决方案可以使用类/属性装饰器以更好的方式编写 .
序列化总是一个问题,在我看来,最好的方法是一种不是最短的方式 . 在所有选项中,这是我更喜欢的,因为类的作者可以完全控制反序列化对象的状态 . 如果我不得不猜测,我会说所有其他选项,迟早会让你遇到麻烦(除非Javascript提出了一个本地方式来解决这个问题) .
实际上,下面的例子没有做到灵活正义 . 它确实只是复制了类的结构 . 但是,你必须要记住的是,该类可以完全控制使用它想要控制整个类状态的任何类型的JSON(你可以计算等等) .
TLDR: TypedJSON (working proof of concept)
这个问题的复杂性的根源是我们需要在运行时使用仅在编译时存在的类型信息来反序列化JSON . 这要求类型信息以某种方式在运行时可用 .
幸运的是,这可以通过decorators和ReflectDecorators以非常优雅和强大的方式解决:
对要进行序列化的属性使用property decorators,以记录元数据信息并将该信息存储在某处,例如在类原型上
将此元数据信息提供给递归初始值设定项(解串器)
录制类型 - 信息
通过ReflectDecorators和属性装饰器的组合,可以轻松记录关于属性的类型信息 . 这种方法的基本实施将是:
对于任何给定的属性,上面的代码片段会将属性的构造函数的引用添加到类原型上的隐藏
__propertyTypes__
属性 . 例如:就是这样,我们在运行时拥有所需的类型信息,现在可以对其进行处理 .
处理类型 - 信息
我们首先需要使用
JSON.parse
获取Object
实例 - 之后,我们可以迭代__propertyTypes__
(上面收集的)中的entires并相应地实例化所需的属性 . 必须指定根对象的类型,以便反序列化器具有起始点 .同样,这种方法的简单实现将是:
以上的想法具有通过预期类型(对于复杂/对象值)反序列化的巨大优势,而不是JSON中存在的类型 . 如果需要
Person
,则它是创建的Person
实例 . 通过对原始类型和数组采取一些额外的安全措施,可以使这种方法安全,抵御任何恶意JSON .边缘案例
但是,如果您现在对解决方案很简单感到高兴,我会有一些坏消息:需要处理大量边缘情况 . 只有一部分是:
数组和数组元素(特别是在嵌套数组中)
多态性
抽象类和接口
......
如果你不愿意,我很乐意推荐一个使用这种方法的概念验证的实验版本,我为解决这个问题而创建了这个问题,这是我每天面对的一个问题 .
由于装饰器仍然被认为是实验性的,我不建议将它用于 生产环境 用途,但到目前为止它对我很有帮助 .
我一直用这个人来做这个工作:https://github.com/weichx/cerialize
它非常简单但功能强大 . 它支持:
整个对象树的序列化和反序列化 .
同一对象上的持久性和瞬态属性 .
用于自定义(反)序列化逻辑的挂钩 .
它可以(反)序列化为现有实例(非常适合Angular)或生成新实例 .
等
例:
我创建了一个生成TypeScript接口的工具和一个运行时"type map",用于对
JSON.parse
的结果执行运行时类型检查:ts.quicktype.io例如,给定此JSON:
quicktype生成以下TypeScript接口并键入map:
然后我们针对类型映射检查
JSON.parse
的结果:我遗漏了一些代码,但您可以尝试quicktype了解详细信息 .
选项#5:使用Typescript构造函数和jQuery.extend
这似乎是最易于维护的方法:添加一个构造函数,该构造函数将json结构作为参数,并扩展json对象 . 这样您就可以将json结构解析为整个应用程序模型 .
无需在构造函数中创建接口或列出属性 .
在您收到公司计算工资的ajax回调中:
上面描述的第四个选项是一个简单而好的方法,在必须处理类层次结构的情况下必须与第二个选项结合,例如成员列表,它是任何子类的出现 . 会员超级会员,例如,会员延伸会员或学生延伸会员 . 在这种情况下,您必须以json格式提供子类类型
也许不是实际的,但简单的解决方案
为困难的依赖工作!
JQuery .extend为您做到这一点:
另一种选择使用工厂
https://github.com/MrAntix/ts-deserialize
像这样用
使您的课程变得简单
注射可供工厂灵活使用
对于简单对象,我喜欢这种方法:
利用在构造函数中定义属性的能力使其简洁 .
这会为您提供一个类型化对象(与使用Object.assign或某些变体的所有答案相比,它们会为您提供一个Object)并且不需要外部库或装饰器 .