JSON到TypeScript类的实例?

时间:2015-04-20 21:29:51

标签: json typescript deserialization

我做了很多研究,但我对我发现的东西并不完全满意。只是为了确定这是我的问题: 什么是将JSON反序列化为TypeScript运行时类实例的最强大,最优雅的自动化解决方案?

说我上了这堂课:

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

并说我有这个用于反序列化的JSON字符串:

{"name": "John Doe"}

获取Foo类实例的最佳和最易维护的解决方案是什么,名称设置为“John Doe”,方法GetName()可以使用?我非常具体地问,因为我知道很容易反序列化为纯数据对象。我想知道是否可以使用工作方法获取类实例,而无需进行任何手动解析或任何手动数据复制。如果无法实现全自动解决方案,那么下一个最佳解决方案是什么?

5 个答案:

答案 0 :(得分:65)

这个问题非常广泛,所以我将提供一些解决方案。

解决方案1:帮助方法

以下是使用帮助程序方法的示例,您可以根据自己的需要进行更改:

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";

解决方案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);

使用基类实现自定义反序列化的方法太多了,所以我会把它留给你想要的方式。

答案 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.assignECMAScript 2015的一部分,目前在大多数现代浏览器中都可用。

答案 2 :(得分:13)

  

对于将JSON反序列化为TypeScript运行时类实例,实际上最强大,最优雅的自动化解决方案是什么?

使用property decoratorsReflectDecorators来记录可在反序列化过程中使用的运行时可访问类型信息,这提供了一种令人惊讶的清洁和广泛适应性的方法,它也适用于现有代码精美。它也是完全自动化的,也适用于嵌套对象。

这个想法的实现是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