我从对REST服务器的AJAX调用中接收到一个JSON对象。这个对象的属性名与我的TypeScript类相匹配(这是这个问题的后续)。
初始化它的最佳方法是什么?我不认为这将工作,因为类(& JSON对象)的成员是对象的列表和成员是类,而这些类的成员是列表和/或类。
但我更喜欢一种方法,查找成员名和分配他们,创建列表和实例化类的需要,所以我不必为每个类中的每个成员写显式代码(有很多!)
我从对REST服务器的AJAX调用中接收到一个JSON对象。这个对象的属性名与我的TypeScript类相匹配(这是这个问题的后续)。
初始化它的最佳方法是什么?我不认为这将工作,因为类(& JSON对象)的成员是对象的列表和成员是类,而这些类的成员是列表和/或类。
但我更喜欢一种方法,查找成员名和分配他们,创建列表和实例化类的需要,所以我不必为每个类中的每个成员写显式代码(有很多!)
当前回答
JQuery .extend为你做这个:
var mytsobject = new mytsobject();
var newObj = {a:1,b:2};
$.extend(mytsobject, newObj); //mytsobject will now contain a & b
其他回答
你可以使用Object。我不知道这是什么时候添加的,我目前使用的是Typescript 2.0.2,这似乎是ES6的一个特性。
client.fetch( '' ).then( response => {
return response.json();
} ).then( json => {
let hal : HalJson = Object.assign( new HalJson(), json );
log.debug( "json", hal );
这是HalJson
export class HalJson {
_links: HalLinks;
}
export class HalLinks implements Links {
}
export interface Links {
readonly [text: string]: Link;
}
export interface Link {
readonly href: URL;
}
这是chrome说的
HalJson {_links: Object}
_links
:
Object
public
:
Object
href
:
"http://localhost:9000/v0/public
你可以看到它没有递归地赋值
选项#5:使用Typescript构造函数和jQuery.extend
这似乎是最可维护的方法:添加一个以json结构作为参数的构造函数,并扩展json对象。这样就可以将json结构解析为整个应用程序模型。
不需要创建接口,或者在构造函数中列出属性。
export class Company
{
Employees : Employee[];
constructor( jsonData: any )
{
jQuery.extend( this, jsonData);
// apply the same principle to linked objects:
if ( jsonData.Employees )
this.Employees = jQuery.map( jsonData.Employees , (emp) => {
return new Employee ( emp ); });
}
calculateSalaries() : void { .... }
}
export class Employee
{
name: string;
salary: number;
city: string;
constructor( jsonData: any )
{
jQuery.extend( this, jsonData);
// case where your object's property does not match the json's:
this.city = jsonData.town;
}
}
在你的ajax回调中,你收到一个公司来计算工资:
onReceiveCompany( jsonCompany : any )
{
let newCompany = new Company( jsonCompany );
// call the methods on your newCompany object ...
newCompany.calculateSalaries()
}
如果你想要类型安全,不喜欢装饰器
abstract class IPerson{
name?: string;
age?: number;
}
class Person extends IPerson{
constructor({name, age}: IPerson){
super();
this.name = name;
this.age = age;
}
}
const json = {name: "ali", age: 80};
const person = new Person(json);
或者我更喜欢这个
class Person {
constructor(init?: Partial<Person>){
Object.assign(this, init);
}
name?: string;
age?: number;
}
const json = {name: "ali", age: 80};
const person = new Person(json);
上面描述的第4个选项是一种简单而漂亮的方法,它必须与第二个选项相结合,在这种情况下,您必须处理一个类层次结构,例如成员列表,它是成员超类的任何一个子类,例如Director extends member或Student extends member。在这种情况下,你必须以json格式给出子类类型
这是一些快速的镜头,展示了几种不同的方法。他们绝不是“完整的”,作为一个免责声明,我不认为这样做是一个好主意。此外,代码不是太干净,因为我只是键入它在一起相当快。
另外需要注意的是:当然,可反序列化的类需要有默认构造函数,就像我所知道的所有其他语言中的反序列化一样。当然,如果你调用一个不带参数的非默认构造函数,Javascript不会抱怨,但是类最好为此做好准备(另外,它不是真正的“typescript方式”)。
选项#1:根本没有运行时信息
这种方法的问题主要在于任何成员的名称必须与其类匹配。它自动地限制你每个类只能有一个相同类型的成员,并打破了一些良好实践的规则。我强烈反对这样做,但这里只列出它,因为这是我写这个答案时的第一份“草稿”(这也是为什么名字是“Foo”等)。
module Environment {
export class Sub {
id: number;
}
export class Foo {
baz: number;
Sub: Sub;
}
}
function deserialize(json, environment, clazz) {
var instance = new clazz();
for(var prop in json) {
if(!json.hasOwnProperty(prop)) {
continue;
}
if(typeof json[prop] === 'object') {
instance[prop] = deserialize(json[prop], environment, environment[prop]);
} else {
instance[prop] = json[prop];
}
}
return instance;
}
var json = {
baz: 42,
Sub: {
id: 1337
}
};
var instance = deserialize(json, Environment, Environment.Foo);
console.log(instance);
选项#2:name属性
为了解决选项1中的问题,我们需要了解JSON对象中节点的类型。问题是在Typescript中,这些东西是编译时构造,我们在运行时需要它们——但运行时对象在设置它们之前根本不知道它们的属性。
一种方法是让类知道它们的名称。不过,JSON中也需要这个属性。实际上,你只需要在json中使用它:
module Environment {
export class Member {
private __name__ = "Member";
id: number;
}
export class ExampleClass {
private __name__ = "ExampleClass";
mainId: number;
firstMember: Member;
secondMember: Member;
}
}
function deserialize(json, environment) {
var instance = new environment[json.__name__]();
for(var prop in json) {
if(!json.hasOwnProperty(prop)) {
continue;
}
if(typeof json[prop] === 'object') {
instance[prop] = deserialize(json[prop], environment);
} else {
instance[prop] = json[prop];
}
}
return instance;
}
var json = {
__name__: "ExampleClass",
mainId: 42,
firstMember: {
__name__: "Member",
id: 1337
},
secondMember: {
__name__: "Member",
id: -1
}
};
var instance = deserialize(json, Environment);
console.log(instance);
选项#3:显式声明成员类型
如上所述,类成员的类型信息在运行时不可用——除非我们使它可用。我们只需要对非原始成员这样做,就可以了:
interface Deserializable {
getTypes(): Object;
}
class Member implements Deserializable {
id: number;
getTypes() {
// since the only member, id, is primitive, we don't need to
// return anything here
return {};
}
}
class ExampleClass implements Deserializable {
mainId: number;
firstMember: Member;
secondMember: Member;
getTypes() {
return {
// this is the duplication so that we have
// run-time type information :/
firstMember: Member,
secondMember: Member
};
}
}
function deserialize(json, clazz) {
var instance = new clazz(),
types = instance.getTypes();
for(var prop in json) {
if(!json.hasOwnProperty(prop)) {
continue;
}
if(typeof json[prop] === 'object') {
instance[prop] = deserialize(json[prop], types[prop]);
} else {
instance[prop] = json[prop];
}
}
return instance;
}
var json = {
mainId: 42,
firstMember: {
id: 1337
},
secondMember: {
id: -1
}
};
var instance = deserialize(json, ExampleClass);
console.log(instance);
选项4:冗长但简洁的方式
2016年3月1日更新:正如@GameAlchemist在评论(想法,实现)中指出的那样,在Typescript 1.7中,下面描述的解决方案可以使用类/属性装饰器以更好的方式编写。
串行化总是一个问题,在我看来,最好的方法不是最短的方法。在所有选项中,这是我更喜欢的选项,因为类的作者可以完全控制反序列化对象的状态。如果要我猜的话,我会说所有其他的选择,迟早都会给你带来麻烦(除非Javascript有一个原生的方法来处理这个问题)。
实际上,下面的例子并没有充分体现灵活性。它实际上只是复制了类的结构。不过,在这里您必须记住的区别是,类可以完全控制使用任何类型的JSON来控制整个类的状态(您可以计算东西等)。
interface Serializable<T> {
deserialize(input: Object): T;
}
class Member implements Serializable<Member> {
id: number;
deserialize(input) {
this.id = input.id;
return this;
}
}
class ExampleClass implements Serializable<ExampleClass> {
mainId: number;
firstMember: Member;
secondMember: Member;
deserialize(input) {
this.mainId = input.mainId;
this.firstMember = new Member().deserialize(input.firstMember);
this.secondMember = new Member().deserialize(input.secondMember);
return this;
}
}
var json = {
mainId: 42,
firstMember: {
id: 1337
},
secondMember: {
id: -1
}
};
var instance = new ExampleClass().deserialize(json);
console.log(instance);