如何将JSON对象转换为TypeScript类?

我从远程REST服务器读取了一个JSON对象。这个JSON对象具有typescript类的所有属性(根据设计)。我如何转换收到的JSON对象的类型var?

我不想填充一个typescript变量(即有一个构造函数,以这个JSON对象)。它很大,在子对象之间复制所有内容&一处一处地查要花很多时间。

但是你可以强制转换为typescript接口!

743754 次浏览

你不能简单地将Ajax请求的原始JavaScript结果转换为典型的JavaScript/TypeScript类实例。有许多技术可以做到这一点,通常涉及到复制数据。除非创建类的实例,否则它不会有任何方法或属性。它仍然是一个简单的JavaScript对象。

而如果你只处理数据,你可以只做一个转换到一个接口(因为它是一个纯粹的编译时结构),这将需要你使用一个TypeScript类,它使用数据实例并对该数据执行操作。

一些复制数据的例子:

  1. 复制AJAX JSON对象到现有对象
  2. 在JavaScript中将JSON字符串解析为特定对象原型

本质上,你只需要:

var d = new MyRichObject();
d.copyInto(jsonResult);

在TypeScript中,你可以使用接口和泛型来实现类型的断言:

var json = Utilities.JSONLoader.loadFromFile("../docs/location_map.json");
var locations: Array<ILocationMap> = JSON.parse(json).location;

其中ILocationMap描述了数据的形状。这种方法的优点是JSON可以包含更多属性,但形状满足接口的条件。

但是,这不会添加类实例方法。

我发现了一篇关于将JSON转换为Typescript类的非常有趣的文章:

http://cloudmark.github.io/Json-Mapping/ < a href = " http://cloudmark.github.io/Json-Mapping/ " > < / >

你最终会得到如下代码:

let example = {
"name": "Mark",
"surname": "Galea",
"age": 30,
"address": {
"first-line": "Some where",
"second-line": "Over Here",
"city": "In This City"
}
};


MapUtils.deserialize(Person, example);  // custom class

我也有同样的问题,我已经找到了一个库来完成这项工作:https://github.com/pleerock/class-transformer

它是这样工作的:

let jsonObject = response.json() as Object;
let fooInstance = plainToClass(Models.Foo, jsonObject);
return fooInstance;

它支持嵌套的子类,但是你必须修饰你的类成员。

虽然它本身不是铸造;我发现https://github.com/JohnWhiteTB/TypedJSON是一个有用的替代方案。

@JsonObject
class Person {
@JsonMember
firstName: string;


@JsonMember
lastName: string;


public getFullname() {
return this.firstName + " " + this.lastName;
}
}
var person = TypedJSON.parse('{ "firstName": "John", "lastName": "Doe" }', Person);


person instanceof Person; // true
person.getFullname(); // "John Doe"

假设json具有与typescript类相同的属性,你不需要将json属性复制到typescript对象中。你只需要在构造函数中传递json数据来构造Typescript对象。

在你的ajax回调中,你会收到一个company:

onReceiveCompany( jsonCompany : any )
{
let newCompany = new Company( jsonCompany );


// call the methods on your newCompany object ...
}

为了使它起作用:

1)在Typescript类中添加一个构造函数,将json数据作为参数。在构造函数中,使用jQuery扩展json对象,如下所示:$.extend( this, jsonData)。美元。扩展允许保留javascript原型,同时添加json对象的属性。

2)注意你必须对链接对象做同样的事情。在本例中的Employees中,还创建了一个构造函数,用于接收Employees的json数据部分。你调用$。将json雇员转换为typescript雇员对象。

export class Company
{
Employees : Employee[];


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


if ( jsonData.Employees )
this.Employees = $.map( jsonData.Employees , (emp) => {
return new Employee ( emp );  });
}
}


export class Employee
{
name: string;
salary: number;


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

这是我在处理Typescript类和json对象时发现的最佳解决方案。

如果你正在使用ES6,试试这个:

class Client{
name: string


displayName(){
console.log(this.name)
}
}


service.getClientFromAPI().then(clientData => {
  

// Here the client data from API only have the "name" field
// If we want to use the Client class methods on this data object we need to:
let clientWithType = Object.assign(new Client(), clientData)


clientWithType.displayName()
})

但是这个方法不会在嵌套对象上工作,很遗憾。

在我的情况下是有效的。我使用函数 # EYZ0。 首先,创建正确的对象,然后将数据从json对象复制到目标。例子:< / p >

let u:User = new User();
Object.assign(u , jsonUsers);

还有一个更高级的使用例子。一个使用数组的例子。

this.someService.getUsers().then((users: User[]) => {
this.users = [];
for (let i in users) {
let u:User = new User();
Object.assign(u , users[i]);
this.users[i] = u;
console.log("user:" + this.users[i].id);
console.log("user id from function(test it work) :" + this.users[i].getId());
}


});


export class User {
id:number;
name:string;
fullname:string;
email:string;


public getId(){
return this.id;
}
}

TLDR:一个班轮

// This assumes your constructor method will assign properties from the arg.
.map((instanceData: MyClass) => new MyClass(instanceData));

详细回答

我会推荐不< em > < / em >对象。赋值方法,因为它会不恰当地用类本身中没有声明的不相关属性(以及定义的闭包)丢弃类实例。

在您试图反序列化的类中,我将确保您想要反序列化的任何属性都已定义(null,空数组等)。通过用初始值定义属性,在尝试迭代要赋值的类成员时暴露了它们的可见性(请参阅下面的反序列化方法)。

export class Person {
public name: string = null;
public favoriteSites: string[] = [];


private age: number = null;
private id: number = null;
private active: boolean;


constructor(instanceData?: Person) {
if (instanceData) {
this.deserialize(instanceData);
}
}


private deserialize(instanceData: Person) {
// Note this.active will not be listed in keys since it's declared, but not defined
const keys = Object.keys(this);


for (const key of keys) {
if (instanceData.hasOwnProperty(key)) {
this[key] = instanceData[key];
}
}
}
}

在上面的例子中,我简单地创建了一个反序列化方法。在实际示例中,我将它集中在可重用基类或服务方法中。

这里是如何利用这在一些像http响应…

this.http.get(ENDPOINT_URL)
.map(res => res.json())
.map((resp: Person) => new Person(resp) ) );

如果tslint/ide抱怨参数类型不兼容,只需使用角括号<YourClassName>将参数转换为相同的类型,例如:

const person = new Person(<Person> { name: 'John', age: 35, id: 1 });

如果你有特定类型的类成员(又名:另一个类的实例),那么你可以通过getter/setter方法将它们强制转换为类型化实例。

export class Person {
private _acct: UserAcct = null;
private _tasks: Task[] = [];


// ctor & deserialize methods...


public get acct(): UserAcct {
return this.acct;
}
public set acct(acctData: UserAcct) {
this._acct = new UserAcct(acctData);
}


public get tasks(): Task[] {
return this._tasks;
}


public set tasks(taskData: Task[]) {
this._tasks = taskData.map(task => new Task(task));
}
}

上面的例子将把acct和任务列表反序列化到它们各自的类实例中。

这是一个老问题,答案基本正确,但不是很有效。我的建议是:

创建一个基类,其中包含init ()方法和静态强制转换方法(用于单个对象和数组)。静态方法可以在任何地方;带有基类和init ()的版本允许随后轻松扩展。

export class ContentItem {
// parameters: doc - plain JS object, proto - class we want to cast to (subclass of ContentItem)
static castAs<T extends ContentItem>(doc: T, proto: typeof ContentItem): T {
// if we already have the correct class skip the cast
if (doc instanceof proto) { return doc; }
// create a new object (create), and copy over all properties (assign)
const d: T = Object.create(proto.prototype);
Object.assign(d, doc);
// reason to extend the base class - we want to be able to call init() after cast
d.init();
return d;
}
// another method casts an array
static castAllAs<T extends ContentItem>(docs: T[], proto: typeof ContentItem): T[] {
return docs.map(d => ContentItem.castAs(d, proto));
}
init() { }
}

类似的机制(分配())已经在@Adam111p的帖子中提到过。只是另一种(更完整的)方法。@Timothy Perez批评分配(),但恕我直言,这在这里是完全合适的。

实现一个派生类(实类):

import { ContentItem } from './content-item';


export class SubjectArea extends ContentItem {
id: number;
title: string;
areas: SubjectArea[]; // contains embedded objects
depth: number;


// method will be unavailable unless we use cast
lead(): string {
return '. '.repeat(this.depth);
}


// in case we have embedded objects, call cast on them here
init() {
if (this.areas) {
this.areas = ContentItem.castAllAs(this.areas, SubjectArea);
}
}
}

现在我们可以强制转换从service检索到的对象:

const area = ContentItem.castAs<SubjectArea>(docFromREST, SubjectArea);

SubjectArea对象的所有层次结构都将具有正确的类。

用例/例子;创建一个Angular服务(还是抽象基类):

export abstract class BaseService<T extends ContentItem> {
BASE_URL = 'http://host:port/';
protected abstract http: Http;
abstract path: string;
abstract subClass: typeof ContentItem;


cast(source: T): T {
return ContentItem.castAs(source, this.subClass);
}
castAll(source: T[]): T[] {
return ContentItem.castAllAs(source, this.subClass);
}


constructor() { }


get(): Promise<T[]> {
const value = this.http.get(`${this.BASE_URL}${this.path}`)
.toPromise()
.then(response => {
const items: T[] = this.castAll(response.json());
return items;
});
return value;
}
}

用法变得非常简单;创建Area服务:

@Injectable()
export class SubjectAreaService extends BaseService<SubjectArea> {
path = 'area';
subClass = SubjectArea;


constructor(protected http: Http) { super(); }
}

服务的get ()方法将返回一个已转换为SubjectArea对象的数组的Promise(整个层次结构)

现在,我们有另一个类:

export class OtherItem extends ContentItem {...}

创建一个检索数据并转换为正确类的服务非常简单:

@Injectable()
export class OtherItemService extends BaseService<OtherItem> {
path = 'other';
subClass = OtherItem;


constructor(protected http: Http) { super(); }
}

目前还没有自动检查从服务器接收到的JSON对象是否具有预期的(读取是否符合)typescript的接口属性。但是您可以使用自定义类型防护

考虑以下接口和一个愚蠢的json对象(它可以是任何类型):

interface MyInterface {
key: string;
}


const json: object = { "key": "value" }

三种可能的方法:

A.类型断言或放置在变量之后的简单静态强制转换

const myObject: MyInterface = json as MyInterface;

B.简单的静态铸造,前变量和菱形之间

const myObject: MyInterface = <MyInterface>json;

C.高级动态转换,你检查自己的对象结构

function isMyInterface(json: any): json is MyInterface {
// silly condition to consider json as conform for MyInterface
return typeof json.key === "string";
}


if (isMyInterface(json)) {
console.log(json.key)
}
else {
throw new Error(`Expected MyInterface, got '${json}'.`);
}

你可以看看这个例子

注意,这里的困难在于编写isMyInterface函数。我希望TS迟早会在运行时导出复杂类型中添加一个装饰器,并让运行时在需要时检查对象的结构。现在,您可以使用json模式验证器,其目的大致相同,也可以使用运行时类型检查函数生成器

这是一个简单而又非常好的选择

let person = "{"name":"Sam","Age":"30"}";


const jsonParse: ((key: string, value: any) => any) | undefined = undefined;
let objectConverted = JSON.parse(textValue, jsonParse);

然后你会得到

objectConverted.name

我在这里使用这个库:https://github.com/pleerock/class-transformer

<script lang="ts">
import { plainToClass } from 'class-transformer';
</script>

实现:

private async getClassTypeValue() {
const value = await plainToClass(ProductNewsItem, JSON.parse(response.data));
}

有时必须解析plainToClass的JSON值才能理解它是JSON格式的数据

在后期TS,你可以这样做:

const isMyInterface = (val: any): val is MyInterface => {
if (!val) { return false; }
if (!val.myProp) { return false; }
return true;
};

而用户是这样的:

if (isMyInterface(data)) {
// now data will be type of MyInterface
}

您可以创建自己类型的interface (SomeType)并在其中强制转换对象。

const typedObject: SomeType = <SomeType> responseObject;
我也遇到过类似的需求。 我想要一些能够让我轻松地从/转换到JSON的东西 这来自于对特定类定义的REST api调用。 我已经找到的解决方案是不够的,或者意味着重写我的 类的代码和添加注释或类似内容

我想在Java中使用类似GSON的东西来序列化/反序列化类到JSON对象。

结合后来的需要,转换器也可以在JS中运行,我结束了编写自己的包。

它有一些开销。但启动后,添加和编辑非常方便。

初始化模块:

  1. 转换模式-允许字段之间的映射和确定 转换将如何完成
  2. 类映射数组
  3. 转换函数映射-用于特殊转换。

然后在你的代码中,你像这样使用初始化的模块:

const convertedNewClassesArray : MyClass[] = this.converter.convert<MyClass>(jsonObjArray, 'MyClass');


const convertedNewClass : MyClass = this.converter.convertOneObject<MyClass>(jsonObj, 'MyClass');

或者,转换为JSON:

const jsonObject = this.converter.convertToJson(myClassInstance);


使用这个链接到npm包,以及如何使用模块的详细说明:json-class-converter

也为
换行 Angular的用法: # EYZ0 < / p >

将对象原样传递给类构造函数;没有约定或检查

interface iPerson {
name: string;
age: number;
}


class Person {
constructor(private person: iPerson) { }


toString(): string {
return this.person.name + ' is ' + this.person.age;
}
}




// runs this as //
const object1 = { name: 'Watson1', age: 64 };
const object2 = { name: 'Watson2' };            // age is missing


const person1 = new Person(object1);
const person2 = new Person(object2 as iPerson); // now matches constructor


console.log(person1.toString())  // Watson1 is 64
console.log(person2.toString())  // Watson2 is undefined

使用从接口扩展的类。

然后:

    Object.assign(
new ToWhat(),
what
)

和最好的:

    Object.assign(
new ToWhat(),
<IDataInterface>what
)

ToWhat变成DataInterface的控制器

你可以使用这个npm包。# EYZ0

它很容易使用,例如:

class UserModel {
@property('i')
id: number;


@property('n')
name: string;
}


const userRaw = {
i: 1234,
n: 'name',
};


// use toClass to convert plain object to class
const userModel = toClass(userRaw, UserModel);
// you will get a class, just like below one
// const userModel = {
//   id: 1234,
//   name: 'name',
// }

如果你需要将json对象转换为typescript类,并在结果对象中使用它的实例方法,你需要使用Object.setPrototypeOf,就像我在下面的代码片段中所做的那样:

Object.setPrototypeOf(jsonObject, YourTypescriptClass.prototype)

https://jvilk.com/MakeTypes/

您可以使用该网站为您生成代理。它生成一个类,可以解析和验证输入的JSON对象。

就我个人而言,我觉得typescript不允许端点定义指定是令人震惊的 接收到的对象的类型。看来情况确实如此, 我会做我对其他语言做过的事情,那就是我将JSON对象从类定义中分离出来, 并让类定义使用JSON对象作为其唯一的数据成员。< / p >

我不喜欢样板代码,所以对我来说,这通常是在保留类型的同时用最少的代码得到想要的结果。

考虑以下JSON对象结构定义——这些将是您在端点处接收到的内容,它们只是结构定义,没有方法。

interface IAddress {
street: string;
city: string;
state: string;
zip: string;
}


interface IPerson {
name: string;
address: IAddress;
}
如果我们用面向对象的术语来考虑上面的内容,那么上面的接口不是类,因为它们只定义了一个数据结构。 OO术语中的类定义了数据和对其进行操作的代码

因此,我们现在定义了一个类,它指定数据和对其进行操作的代码……

class Person {
person: IPerson;


constructor(person: IPerson) {
this.person = person;
}


// accessors
getName(): string {
return person.name;
}


getAddress(): IAddress {
return person.address;
}


// You could write a generic getter for any value in person,
// no matter how deep, by accepting a variable number of string params


// methods
distanceFrom(address: IAddress): float {
// Calculate distance from the passed address to this persons IAddress
return 0.0;
}
}

现在我们可以简单地传入任何符合IPerson结构的对象,然后继续我们的方法…

   Person person = new Person({
name: "persons name",
address: {
street: "A street address",
city: "a city",
state: "a state",
zip: "A zipcode"
}
});

以同样的方式,我们现在可以处理从您的端点接收到的对象,内容如下所示:

Person person = new Person(req.body);    // As in an object received via a POST call


person.distanceFrom({ street: "Some street address", etc.});
这在性能上要高得多,并且使用了复制数据时一半的内存,同时显著减少了必须为每种实体类型编写的样板代码的数量。 它仅仅依赖于TypeScript提供的类型安全性。< / p >

使用“as”声明:

const data = JSON.parse(response.data) as MyClass;

您可以像这样将json转换为属性

class Jobs {
constructor(JSONdata) {
this.HEAT = JSONdata.HEAT;
this.HEAT_EAF = JSONdata.HEAT_EAF;
}
  

}


var job = new Jobs({HEAT:'123',HEAT_EAF:'456'});
你可以用一个tapi.js!

npm i -D tapi.js

然后你就可以简单地做了

let typedObject = new YourClass().fromJSON(jsonData)

或者用承诺

axios.get(...).as(YourClass).then(typedObject => { ... })

你可以在的文档上阅读更多关于它的信息。

有几种方法可以做到这一点,让我们来看看一些选项:

class Person {
id: number | undefined;
firstName: string | undefined;
//? mark for note not required attribute.
lastName?: string;
}


// Option 1: Fill any attribute and it would be accepted.
const person1= { firstName: 'Cassio' } as Person ;
console.log(person1);


// Option 2. All attributes must assign data.
const  person2: Person = { id: 1, firstName: 'Cassio', lastName:'Seffrin' };
console.log(person2);


//  Option 3. Use partial interface if all attribute not required.
const  person3: Partial<Person> = { firstName: 'Cassio' };
console.log(person3);


//  Option 4. As lastName is optional it will work
const  person4: Person = { id:2, firstName: 'Cassio'  };
console.log(person4);


//  Option 5. Fill any attribute and it would be accepted.
const person5 = <Person> {firstName: 'Cassio'};
console.log(person5 );

结果:

[LOG]: {
"firstName": "Cassio"
}
[LOG]: {
"id": 1,
"firstName": "Cassio",
"lastName": "Seffrin"
}
[LOG]: {
"firstName": "Cassio"
}
[LOG]: {
"id": 2,
"firstName": "Cassio"
}
[LOG]: {
"firstName": "Cassio"
}

如果你有一个接口而不是Typescript类,它也可以工作。

interface PersonInterface {
id: number;
firstName: string;
lastName?: string;
}

<一个href = " https://www.typescriptlang.org/play?jsx=0代码/ MYGwhgzhAEAKCmAnCB7AdtA3gKGnglgCYBc0aArgLYBGS0APtOWofAGb5ryEDcueHZABcAcmErxSEIYk4BzBkxbtO3PnmgB6TQH5olMIgDW0NikRkUQ + JaHRE8AI7l8DwtDBCZ + auWsA6fmhwaTEJHSlvNDk + AF9sbG1oAHkAByF8dGgARlIAMXwQEA80AE8PL1lfaxL3fDsAdxRyEHdaD2BgeHTuQOB0aWhUpFQ0bIBeLFNXUPFJaAByAGFICEyF6FiPGARkLL5 + tFQQeH8QFDkACmG9sYBKPkTNFPTMjAAmf2gAQSKK72q8BglHIg1W + DkGEInjAfQGdiGI3Q71Iu1G0EmmGgRFI2QANNNhGF5stwSgFgSQqI5sQFgBldhsWRoDaxHh4bCHY6nc5XG6jd4PBJJF4ZLIAZi + oJsqUMGTAxU41kQbDAXWxbA8f08AL8NjQVnsThcbjhRwR-PQ4tRcvwCoAPGj0AA+DFTQSzCSkUlQdabdnQTkDFAnM4Xa5ItDioVPPBpMUYAAsX2+ mcpxoxmbqr3qcuxjukxsaxid5rwllrqidrkbdwjx7wjhupxswk195lwbnl3ldfmjizjivjb2gafyvgvtwv - lu9bv89ami02jy1v0eoqzymk6o3y7i67me3id622tywza1yqzzwzvodhhc8ajj2bwoad9zfqdpgdbkpbvs6wmajvnubcdnbnzqzugjsharqiegycowhed4rdcu9ezpgzgjyw0dm5giabcpkddnliia " rel="nofollow noreferrer">播放此代码

Java爱好者

创建POJO类

export default class TransactionDTO{
constructor() {
}
}

按类创建空对象

let dto = new TransactionDto()   // ts object
let json = {name:"Kamal",age:40} // js object


let transaction: TransactionDto = Object.assign(dto,JSON.parse(JSON.stringify(json)));//conversion
我认为json2typescript是一个很好的选择 # EYZ0 < / p >

你可以用一个带有注释的简单模型类将json转换为Class模型

用于工程