我做了很多研究,但我对我发现的东西并不完全满意。只是为了确定这是我的问题: 什么是将JSON反序列化为TypeScript运行时类实例的最强大,最优雅的自动化解决方案?
说我上了这堂课:
class Foo {
name: string;
GetName(): string { return this.name };
}
并说我有这个用于反序列化的JSON字符串:
{"name": "John Doe"}
获取Foo类实例的最佳和最易维护的解决方案是什么,名称设置为“John Doe”,方法GetName()可以使用?我非常具体地问,因为我知道很容易反序列化为纯数据对象。我想知道是否可以使用工作方法获取类实例,而无需进行任何手动解析或任何手动数据复制。如果无法实现全自动解决方案,那么下一个最佳解决方案是什么?
答案 0 :(得分:65)
这个问题非常广泛,所以我将提供一些解决方案。
以下是使用帮助程序方法的示例,您可以根据自己的需要进行更改:
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;
}
}
然后使用它:
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";
另一种方法是创建自己的基类:
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);
使用基类实现自定义反序列化的方法太多了,所以我会把它留给你想要的方式。
答案 1 :(得分:42)
您现在可以使用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.assign
是ECMAScript 2015的一部分,目前在大多数现代浏览器中都可用。
答案 2 :(得分:13)
对于将JSON反序列化为TypeScript运行时类实例,实际上最强大,最优雅的自动化解决方案是什么?
使用property decorators和ReflectDecorators来记录可在反序列化过程中使用的运行时可访问类型信息,这提供了一种令人惊讶的清洁和广泛适应性的方法,它也适用于现有代码精美。它也是完全自动化的,也适用于嵌套对象。
这个想法的实现是TypedJSON,我是为这个任务准确创建的:
@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"
答案 3 :(得分:4)
为什么你不能做这样的事情?
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 ...
答案 4 :(得分:-1)
我在处理Typescript类和json对象时找到的最佳解决方案:在Typescript类中添加一个构造函数,该构造函数将json数据作为参数。在该构造函数中,您使用jQuery扩展json对象,如下所示:$ .extend(this,jsonData)。 $ .extend允许在添加json对象的属性时保留javascript原型。
export class Foo
{
Name: string;
getName(): string { return this.Name };
constructor( jsonFoo: any )
{
$.extend( this, jsonFoo);
}
}
在你的ajax回调中,在你的打字稿对象中翻译你的jsons,如下所示:
onNewFoo( jsonFoos : any[] )
{
let receviedFoos = $.map( jsonFoos, (json) => { return new Foo( json ); } );
// then call a method:
let firstFooName = receviedFoos[0].GetName();
}
如果你没有添加构造函数,请在你的ajax回调中调用juste:
let newFoo = new Foo();
$.extend( newFoo, jsonData);
let name = newFoo.GetName()
...但是如果你想转换子json对象,构造函数也会很有用。请参阅我的详细答案here。