【发布时间】:2013-04-28 08:38:34
【问题描述】:
是否有任何方法可以对 Typescript 对象进行 JSON 序列化/反序列化,以便它们不会丢失类型信息?简单的JSON.parse(JSON.stringify) 有太多注意事项。
或者我应该使用临时解决方案?
【问题讨论】:
标签: javascript json serialization typescript
是否有任何方法可以对 Typescript 对象进行 JSON 序列化/反序列化,以便它们不会丢失类型信息?简单的JSON.parse(JSON.stringify) 有太多注意事项。
或者我应该使用临时解决方案?
【问题讨论】:
标签: javascript json serialization typescript
使用接口获取强类型:
// Creating
var foo:any = {};
foo.x = 3;
foo.y='123';
var jsonString = JSON.stringify(foo);
alert(jsonString);
// Reading
interface Bar{
x:number;
y?:string;
}
var baz:Bar = JSON.parse(jsonString);
alert(baz.y);
如果需要,请使用类型断言“”。
【讨论】:
我认为处理这个问题的更好方法是使用 Object.assign(但需要 ECMAScript 2015)。
给定一个班级
class Pet {
name: string;
age: number;
constructor(name?: string, age?: number) {
this.name = name;
this.age = age;
}
getDescription(): string {
return "My pet " + this.name + " is " + this.age + " years old.";
}
static fromJSON(d: Object): Pet {
return Object.assign(new Pet(), d);
}
}
像这样序列化和反序列化...
var p0 = new Pet("Fido", 5);
var s = JSON.stringify(p0);
var p1 = Pet.fromJSON(JSON.parse(s));
console.log(p1.getDescription());
要将此示例更上一层楼,请考虑嵌套对象...
class Type {
kind: string;
breed: string;
constructor(kind?: string, breed?: string) {
this.kind = kind;
this.breed = breed;
}
static fromJSON(d: Object) {
return Object.assign(new Type(), d);
}
}
class Pet {
name: string;
age: number;
type: Type;
constructor(name?: string, age?: number) {
this.name = name;
this.age = age;
}
getDescription(): string {
return "My pet " + this.name + " is " + this.age + " years old.";
}
getFullDescription(): string {
return "My " + this.type.kind + ", a " + this.type.breed + ", is " + this.age + " years old.";
}
static fromJSON(d: Object): Pet {
var o = Object.assign(new Pet(), d);
o.type = Type.fromJSON(o['type']);
return o;
}
}
像这样序列化和反序列化...
var q0 = new Pet("Fido", 5);
q0.type = new Type("dog", "Pomeranian");
var t = JSON.stringify(q0);
var q1 = Pet.fromJSON(JSON.parse(t));
console.log(q1.getFullDescription());
因此与使用接口不同,这种方法保留了方法。
【讨论】:
目前我发现的最好的方法是使用"jackson-js", jackson-js 是一个允许您使用 ts-decorators 描述类的项目 然后序列化和反序列化保存类型信息。 它支持数组、地图等。
简单示例:
import { JsonProperty, JsonClassType, JsonAlias, ObjectMapper } from 'jackson-js';
class Book {
@JsonProperty() @JsonClassType({type: () => [String]})
name: string;
@JsonProperty() @JsonClassType({type: () => [String]})
@JsonAlias({values: ['bkcat', 'mybkcat']})
category: string;
}
class Writer {
@JsonProperty() @JsonClassType({type: () => [Number]})
id: number;
@JsonProperty() @JsonClassType({type: () => [String]})
name: string;
@JsonProperty() @JsonClassType({type: () => [Array, [Book]]})
books: Book[] = [];
}
const objectMapper = new ObjectMapper();
// eslint-disable-next-line max-len
const jsonData = '{"id":1,"name":"John","books":[{"name":"Learning TypeScript","bkcat":"Web Development"},{"name":"Learning Spring","mybkcat":"Java"}]}';
const writer = objectMapper.parse<Writer>(jsonData, {mainCreator: () => [Writer]});
console.log(writer);
/*
Writer {
books: [
Book { name: 'Learning TypeScript', category: 'Web Development' },
Book { name: 'Learning Spring', category: 'Java' }
],
id: 1,
name: 'John'
}
*/
还有一些其他项目声称做同样的事情 -
但是当我使用 Ts Map 时,jackson-js 是唯一对我有用的。
【讨论】:
AQuirky answer 适合我。 Object.assign 方法可能会遇到一些问题。我不得不修改我的 tsconfig.json 以包括:
"compilerOptions": {
...
"lib": ["es2015"],
...
}
【讨论】:
首先,您需要创建一个源实体的接口,该接口从 API 接收为 JSON:
interface UserEntity {
name: string,
age: number,
country_code: string
};
第二次使用构造函数实现模型,您可以在其中自定义(camelize)一些字段名称:
class User {
constructor({ name, age, country_code: countryCode }: UserEntity) {
Object.assign(this, { name, age, countryCode });
}
}
最后使用 JS 对象 jsonUser 创建用户模型的实例”
const jsonUser = {name: 'Ted', age: 2, country_code: 'US'};
const userInstance = new User(jsonUser);
console.log({ userInstance })
【讨论】:
AQuirky's answer 是一个很好的起点,但正如我在评论中提到的,它的主要问题是它需要允许创建具有未定义字段的对象,然后由他的 fromJSON 方法填充。这违反了RAII 原则,并且可能/将迷惑该类的用户,他们可能会陷入创建不完整 Pet 的陷阱(没有明确指出调用不带参数的构造函数后必须调用 fromJSON() 到填充对象)。
因此,在他的回答的基础上,这是一种方法,使用 javascript 的prototype chain,在序列化/反序列化后取回类的对象。关键技巧只是在序列化和反序列化之后重新分配正确的原型对象:
class Foo {}
foo1 = new Foo();
foo2 = JSON.parse(JSON.stringify(p1))
foo2.__proto__ = Foo.prototype;
所以要使用这个技巧修复 AQuirky 的示例,我们可以简单地将他的 fromJSON 函数更改为
static fromJSON(d: Object): Pet {
d.__proto__ = Pet.prototype;
return p
}
【讨论】: