这些是一些关于此的快速截图,以展示几种不同的方式。它们绝不是“完整的”,作为免责声明,我认为这样做不是一个好主意。此外,代码也不是很干净,因为我只是很快将它们一起输入。
另请注意:当然,可反序列化的类需要具有默认构造函数,就像我知道任何类型的反序列化的所有其他语言一样。当然,如果您调用不带参数的非默认构造函数,Javascript 不会抱怨,但类最好为它做好准备(另外,它不会真的是“打字方式”)。
选项 #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 年 1 月 3 日更新: 正如 @GameAlchemist 在 cmets 中指出的那样(idea、implementation),从 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);