JSON 到 TypeScript 类实例?

我做了一些研究,但我对我的发现并不完全满意。只是想确认一下我的问题: 对于将 JSON 反序列化为 TypeScript 运行时类实例,实际上最健壮和最优雅的自动化解决方案是什么?

假设我上了这门课:

class Foo {
name: string;
GetName(): string { return this.name };
}

假设我得到了这个用于反序列化的 JSON 字符串:

{"name": "John Doe"}

对于将名称设置为“ JohnDoe”并使用 GetName ()方法工作的 Foo 类的实例,什么是最好和最可维护的解决方案?我之所以问得这么具体,是因为我知道反序列化到纯数据对象很容易。我想知道是否有可能获得带有工作方法的类实例,而不必进行任何手动解析或手动数据复制。如果不可能实现完全自动化的解决方案,那么下一个最佳解决方案是什么?

122206 次浏览

这个问题相当宽泛,所以我将给出几个解决方案。

解决方案1: 辅助方法

下面是一个使用 Helper 方法的例子,你可以根据自己的需要进行修改:

class SerializationHelper {
static toInstance<T>(obj: T, json: string) : T {
var jsonObj = JSON.parse(json);


if (typeof obj["fromJSON"] === "function") {
obj["fromJSON"](jsonObj);
}
else {
for (var propName in jsonObj) {
obj[propName] = jsonObj[propName]
}
}


return obj;
}
}

然后使用它:

var json = '{"name": "John Doe"}',
foo = SerializationHelper.toInstance(new Foo(), json);


foo.GetName() === "John Doe";

高级反序列化

这也可以通过将您自己的 fromJSON方法添加到类中来实现一些自定义反序列化(这适用于 JSON.stringify已经使用 toJSON方法的情况,如下所示) :

interface IFooSerialized {
nameSomethingElse: string;
}


class Foo {
name: string;
GetName(): string { return this.name }


toJSON(): IFooSerialized {
return {
nameSomethingElse: this.name
};
}


fromJSON(obj: IFooSerialized) {
this.name = obj.nameSomethingElse;
}
}

Then using it:

var foo1 = new Foo();
foo1.name = "John Doe";


var json = JSON.stringify(foo1);


json === '{"nameSomethingElse":"John Doe"}';


var foo2 = SerializationHelper.toInstance(new Foo(), json);


foo2.GetName() === "John Doe";

解决方案2: 基类

另一种方法是创建自己的基类:

class Serializable {
fillFromJSON(json: string) {
var jsonObj = JSON.parse(json);
for (var propName in jsonObj) {
this[propName] = jsonObj[propName]
}
}
}


class Foo extends Serializable {
name: string;
GetName(): string { return this.name }
}

然后使用它:

var foo = new Foo();
foo.fillFromJSON(json);

使用基类实现自定义反序列化有太多不同的方法,所以我将把它留给您自己决定。

你为什么就不能做这样的事呢?

class Foo {
constructor(myObj){
Object.assign(this, myObj);
}
get name() { return this._name; }
set name(v) { this._name = v; }
}


let foo = new Foo({ name: "bat" });
foo.toJSON() //=> your json ...

What is actually the most robust and elegant automated solution for deserializing JSON to TypeScript runtime class instances?

使用 房屋装修工RefectDecorators 反射装饰器来记录运行时可访问的类型信息,这些信息可以在反序列化过程中使用,这为 surprisingly提供了一种干净和广泛适用的方法,也非常适合现有的代码。它也是完全自动化的,并且也适用于嵌套对象。

这个想法的一个实现是 输入 JSON,它正是我为这个任务创建的:

@JsonObject
class Foo {
@JsonMember
name: string;


getName(): string { return this.name };
}
var foo = TypedJSON.parse('{"name": "John Doe"}', Foo);


foo instanceof Foo; // true
foo.getName(); // "John Doe"

The best solution I found when dealing with Typescript classes and json objects: add a constructor in your Typescript class that takes the json data as parameter. In that constructor you extend your json object with jQuery, like this: $.extend( this, jsonData). $.extend allows keeping the javascript prototypes while adding the json object's properties.

export class Foo
{
Name: string;
getName(): string { return this.Name };


constructor( jsonFoo: any )
{
$.extend( this, jsonFoo);
}
}

In your ajax callback, translate your jsons in a your typescript object like this:

onNewFoo( jsonFoos : any[] )
{
let receviedFoos = $.map( jsonFoos, (json) => { return new Foo( json ); } );


// then call a method:
let firstFooName = receviedFoos[0].GetName();
}

如果不添加构造函数,只需调用 ajax 回调函数:

let newFoo = new Foo();
$.extend( newFoo, jsonData);
let name = newFoo.GetName()

... 但是如果你也想转换子 json 对象,构造函数会很有用。请看我的详细答案 给你

您现在可以使用 Object.assign(target, ...sources):

class Foo {
name: string;
getName(): string { return this.name };
}


let fooJson: string = '{"name": "John Doe"}';
let foo: Foo = Object.assign(new Foo(), JSON.parse(fooJson));


console.log(foo.getName()); //returns John Doe

Object.assignECMAScript 2015的一部分,目前可以在大多数现代浏览器中使用。