class-transformer 中文文档(翻译)
class-transformer (翻译文档)
Table of contents
- 什么是class-transformer
- 安装
- Methods
- 执行类型安全的实例
- 使用嵌套对象
- 暴露getter方法和方法返回值
- 使用不同的名称公开属性
- 跳过特定属性
- 跳越依赖于操作
- 跳过类的所有属性
- 跳过私有属性或一些带前缀的属性
- 使用组来控制被排除的属性
- 使用版本控制来控制公开的和排除的属性
- 转换日期字符串到日期对象
- 使用数组
- 额外的数据转换
- 其他修饰符
- 使用泛型
- 隐式类型转换
- 它如何处理循环引用?
- 在Angular2中使用
- 例子
- 发行说明
什么是class-transformer⬆
在JavaScript中有两种类型的对象:
- plain (literal) objects
- class (constructor) objects
纯对象是`Object”类的实例。 当通过“{}”符号创建时,有时它们被称为文字对象。
那么,问题是什么呢?
有时候你想把普通的javascript对象转换成你拥有的ES6类。
例如,在“users”中有一个用户列表。
[
{
"id": 1,
"firstName": "Johny",
"lastName": "Cage",
"age": 27
},
{
"id": 2,
"firstName": "Ismoil",
"lastName": "Somoni",
"age": 50
},
{
"id": 3,
"firstName": "Luke",
"lastName": "Dacascos",
"age": 12
}
]
你有一个' User '类:
export class User {
id: number;
firstName: string;
lastName: string;
age: number;
getName() {
return this.firstName + ' ' + this.lastName;
}
isAdult() {
return this.age > 36 && this.age < 60;
}
}
假设您正在从“users”下载类型为“User”的用户:
fetch('users.json').then((users: User[]) => {
// you can use users here, and type hinting also will be available to you,
// but users are not actually instances of User class
// this means that you can't use methods of User class
});
在此代码中,您可以使用' users[0] '。id ',你也可以用' users[0]。firstName”和“用户[0].lastName”。
那么该怎么办呢?如何使' User '对象的实例的' users '数组而不是普通的javascript对象?
是的,您可以使用类转换器。这个库的目的是帮助您映射普通的javascript
这个库对于在api中公开的模型也很有用, 这里有个例子:
fetch('users.json').then((users: Object[]) => {
const realUsers = plainToClass(User, users);
// now each user in realUsers is instance of User class
});
现在可以使用' users[0]. getname() '和' users[0]. isadult() '方法。
安装⬆
Node.js⬆
-
安装模块:
npm install class-transformer --save
-
reflect-metadata
是必需的,安装:npm install reflect-metadata --save
确保导入到全局位置,比如app.ts:
import 'reflect-metadata';
-
使用ES6特性,如果您使用旧版本的node.js,您可能需要安装ES6 -shim:
npm install es6-shim --save
然后导入到全局位置,比如app.ts:
import 'es6-shim';
Browser⬆
-
安装模块:
npm install class-transformer --save
-
reflect-metadata
是必需的,安装:npm install reflect-metadata --save
在' index.html '的头部添加 :
<html> <head> <!-- ... --> <script src="node_modules/reflect-metadata/Reflect.js"></script> </head> <!-- ... --> </html>
如果你使用的是angular 2,你应该已经安装了这个垫片。
-
如果你正在使用system.js,你可能想要把它添加到' map '和' package ' config中:
{ "map": { "class-transformer": "node_modules/class-transformer" }, "packages": { "class-transformer": { "main": "index.js", "defaultExtension": "js" } } }
Methods⬆
plainToClass⬆
此方法将普通javascript对象转换为特定类的实例。
import { plainToClass } from 'class-transformer';
let users = plainToClass(User, userJson); // 将用户纯对象转换为单个用户。还支持数组
plainToClassFromExist⬆
该方法使用已填充的对象(目标类的实例)将普通对象转换为实例。
const defaultUser = new User();
defaultUser.role = 'user';
let mixedUser = plainToClassFromExist(defaultUser, user); // 混合后的 user.role = 'user'
classToPlain⬆
这个方法将你的类对象转换回普通的javascript对象,也就是 ' JSON .stringify '。
import { classToPlain } from 'class-transformer';
let photo = classToPlain(photo);
classToClass⬆
此方法将您的类对象转换为类对象的新实例。
import { classToClass } from 'class-transformer';
let photo = classToClass(photo);
你也可以在转换选项中使用ignoreDecorators
选项来忽略你的类正在使用的所有decorator。
serialize⬆
使用 serialize
方法,您可以将您的模型直接序列化为json:
import { serialize } from 'class-transformer';
let photo = serialize(photo);
serialize
可以同时使用与数组和非数组.
deserialize and deserializeArray⬆
使用deserialize
方法,你可以从一个json反序列化为你的模型:
import { deserialize } from 'class-transformer';
let photo = deserialize(Photo, photo);
使反序列化在数组中工作使用 deserializeArray
方法:
import { deserializeArray } from 'class-transformer';
let photos = deserializeArray(Photo, photos);
执行类型安全的实例⬆
plainToClass
方法的默认行为是设置plain对象的所有属性,
import { plainToClass } from 'class-transformer';
class User {
id: number;
firstName: string;
lastName: string;
}
const fromPlainUser = {
unkownProp: 'hello there',
firstName: 'Umed',
lastName: 'Khudoiberdiev',
};
console.log(plainToClass(User, fromPlainUser));
// User {
// unkownProp: 'hello there',
// firstName: 'Umed',
// lastName: 'Khudoiberdiev',
// }
如果此行为不符合您的需要,您可以使用excludeExtraneousValues
选项
import { Expose, plainToClass } from 'class-transformer';
class User {
@Expose() id: number;
@Expose() firstName: string;
@Expose() lastName: string;
}
const fromPlainUser = {
unkownProp: 'hello there',
firstName: 'Umed',
lastName: 'Khudoiberdiev',
};
console.log(plainToClass(User, fromPlainUser, { excludeExtraneousValues: true }));
// User {
// id: undefined,
// firstName: 'Umed',
// lastName: 'Khudoiberdiev'
// }
使用嵌套对象⬆
当你试图转换有嵌套对象的对象时,
它需要知道你要转换的对象的类型。
因为Typescript还没有很好的反射能力,
我们应该隐式地指定每个属性包含的对象类型。
这是使用 @Type
装饰器完成的。
假设我们有一个带有照片的相册,我们正在尝试将相册纯对象转换为类对象:
import { Type, plainToClass } from 'class-transformer';
export class Album {
id: number;
name: string;
@Type(() => Photo)
photos: Photo[];
}
export class Photo {
id: number;
filename: string;
}
let album = plainToClass(Album, albumJson);
// now album is Album object with Photo objects inside
提供多个类型选项⬆
如果嵌套对象可以是不同类型的,您可以提供一个额外的options对象,
它指定了一个鉴别器( property
)。鉴别器选项必须定义一个“属性”来保存子程序
对象的类型名称和可能的“子类型”,嵌套对象可以转换为。子类型
有一个value
,它包含类型的构造函数和name
,可以与 property
匹配。
假设我们有一个相册,上面有一张照片。但这张照片可以是某些不同的类型。
我们试图将普通对象的相册转换为类对象。必须定义普通对象输入
附加属性__type
。默认情况下,这个属性在转换期间被删除:
JSON input:
{
"id": 1,
"name": "foo",
"topPhoto": {
"id": 9,
"filename": "cool_wale.jpg",
"depth": 1245,
"__type": "underwater"
}
}
import { Type, plainToClass } from 'class-transformer';
export abstract class Photo {
id: number;
filename: string;
}
export class Landscape extends Photo {
panorama: boolean;
}
export class Portrait extends Photo {
person: Person;
}
export class UnderWater extends Photo {
depth: number;
}
export class Album {
id: number;
name: string;
@Type(() => Photo, {
discriminator: {
property: '__type',
subTypes: [
{ value: Landscape, name: 'landscape' },
{ value: Portrait, name: 'portrait' },
{ value: UnderWater, name: 'underwater' },
],
},
})
topPhoto: Landscape | Portrait | UnderWater;
}
let album = plainToClass(Album, albumJson);
// now album is Album object with a UnderWater object without `__type` property.
提示:这同样适用于具有不同子类型的数组。此外,你可以指定keepDiscriminatorProperty: true
在选项中,将discriminator
属性也保留在生成的类中。
暴露getter方法和方法返回值⬆
您可以通过为这些getter或方法设置一个@Expose()
装饰器来公开您的getter或方法返回的内容:
import { Expose } from 'class-transformer';
export class User {
id: number;
firstName: string;
lastName: string;
password: string;
@Expose()
get name() {
return this.firstName + ' ' + this.lastName;
}
@Expose()
getFullName() {
return this.firstName + ' ' + this.lastName;
}
}
使用不同的名称公开属性⬆
如果你想用不同的名字公开一些属性,
你可以通过为@Expose
装饰器指定一个name
选项来实现:
import { Expose } from 'class-transformer';
export class User {
@Expose({ name: 'uid' })
id: number;
firstName: string;
lastName: string;
@Expose({ name: 'secretKey' })
password: string;
@Expose({ name: 'fullName' })
getFullName() {
return this.firstName + ' ' + this.lastName;
}
}
跳过特定属性⬆
有时您希望在转换期间跳过某些属性。
这可以通过使用@Exclude
装饰器来完成:
import { Exclude } from 'class-transformer';
export class User {
id: number;
email: string;
@Exclude()
password: string;
}
现在,当您转换User时,password
属性将被跳过,并且不会包含在转换后的结果中。
跳越依赖于操作⬆
使用toClassOnly
或toPlainOnly
选项:您可以控制排除某个属性。
import { Exclude } from 'class-transformer';
export class User {
id: number;
email: string;
@Exclude({ toPlainOnly: true })
password: string;
}
现在,password
属性将只在classToPlain
操作期间被排除。反之,使用toClassOnly
选项。
跳过类的所有属性⬆
您可以跳过类的所有属性,只显式地公开那些需要的:
import { Exclude, Expose } from 'class-transformer';
@Exclude()
export class User {
@Expose()
id: number;
@Expose()
email: string;
password: string;
}
现在id
和email
将被暴露,密码将在转换过程中被排除。
或者,您可以在转换期间设置排除策略:
import { classToPlain } from 'class-transformer';
let photo = classToPlain(photo, { strategy: 'excludeAll' });
在这种情况下,您不需要@Exclude()
整个类。
跳过私有属性或一些带前缀的属性⬆
如果你用前缀来命名你的私有属性,比如 _
,
然后你也可以从转换中排除这些属性:
import { classToPlain } from 'class-transformer';
let photo = classToPlain(photo, { excludePrefixes: ['_'] });
这将跳过所有以 _
开头的属性。
您可以传递任意数量的前缀,并且以这些前缀开头的所有属性都将被忽略。
例如:
import { Expose, classToPlain } from 'class-transformer';
export class User {
id: number;
private _firstName: string;
private _lastName: string;
_password: string;
setName(firstName: string, lastName: string) {
this._firstName = firstName;
this._lastName = lastName;
}
@Expose()
get name() {
return this._firstName + ' ' + this._lastName;
}
}
const user = new User();
user.id = 1;
user.setName('Johny', 'Cage');
user._password = '123';
const plainUser = classToPlain(user, { excludePrefixes: ['_'] });
// here plainUser will be equal to
// { id: 1, name: "Johny Cage" }
使用组来控制被排除的属性⬆
您可以使用组来控制哪些数据会被公开,哪些不会:
import { Exclude, Expose, classToPlain } from 'class-transformer';
export class User {
id: number;
name: string;
@Expose({ groups: ['user', 'admin'] }) // this means that this data will be exposed only to users and admins
email: string;
@Expose({ groups: ['user'] }) // this means that this data will be exposed only to users
password: string;
}
let user1 = classToPlain(user, { groups: ['user'] }); // will contain id, name, email and password
let user2 = classToPlain(user, { groups: ['admin'] }); // will contain id, name and email
使用版本控制来控制公开的和排除的属性⬆
如果你正在构建一个有不同版本的API, class transformer有非常有用的工具。 您可以控制模型的哪些属性应该在哪个版本中公开或排除。例子:
import { Exclude, Expose, classToPlain } from 'class-transformer';
export class User {
id: number;
name: string;
@Expose({ since: 0.7, until: 1 }) // this means that this property will be exposed for version starting from 0.7 until 1
email: string;
@Expose({ since: 2.1 }) // this means that this property will be exposed for version starting from 2.1
password: string;
}
let user1 = classToPlain(user, { version: 0.5 }); // will contain id and name
let user2 = classToPlain(user, { version: 0.7 }); // will contain id, name and email
let user3 = classToPlain(user, { version: 1 }); // will contain id and name
let user4 = classToPlain(user, { version: 2 }); // will contain id and name
let user5 = classToPlain(user, { version: 2.1 }); // will contain id, name and password
转换日期字符串到日期对象⬆
有时,在普通javascript对象中有一个以字符串格式接收的日期。
你想从中创建一个真正的javascript Date对象。
你可以简单地通过传递一个日期对象给 @Type
装饰器来完成:
import { Type } from 'class-transformer';
export class User {
id: number;
email: string;
password: string;
@Type(() => Date)
registrationDate: Date;
}
注意,当您试图将类对象转换为普通对象时,日期将被转换为字符串。
同样的方式也可以用于Number
, String
, Boolean
当您希望将值转换为这些类型时,可以使用基本类型。
使用数组⬆
在使用数组时,必须提供数组包含的对象的类型。
这种类型,您在@Type()
装饰器中指定:
import { Type } from 'class-transformer';
export class Photo {
id: number;
name: string;
@Type(() => Album)
albums: Album[];
}
你也可以使用自定义数组类型:
import { Type } from 'class-transformer';
export class AlbumCollection extends Array<Album> {
// custom array functions ...
}
export class Photo {
id: number;
name: string;
@Type(() => Album)
albums: AlbumCollection;
}
库将自动处理正确的转换。
ES6集合Set
和Map
也需要@Type
装饰器:
export class Skill {
name: string;
}
export class Weapon {
name: string;
range: number;
}
export class Player {
name: string;
@Type(() => Skill)
skills: Set<Skill>;
@Type(() => Weapon)
weapons: Map<string, Weapon>;
}
额外的数据转换⬆
基本用法⬆
您可以使用@Transform
装饰器执行额外的数据转换。
例如,你想让你的 Date
对象成为一个moment
对象(transforming object from plain to class):
import { Transform } from 'class-transformer';
import * as moment from 'moment';
import { Moment } from 'moment';
export class Photo {
id: number;
@Type(() => Date)
@Transform(value => moment(value), { toClassOnly: true })
date: Moment;
}
当你调用plainToClass
转换 Photo object
的普通对象(plain
)时,
它将把对象中的 date
转换为 moment
。
@Transform
装饰器也支持组和版本控制。
高级用法⬆
@Transform
装饰器提供了更多参数,让您可以配置如何完成转换。
@Transform((value, obj, type) => value)
Argument | Description |
---|---|
value | 转换前的属性值. |
obj | 转换源对象。 |
type | 转换类型。 |
其他修饰符⬆
Signature | Example | Description |
---|---|---|
@TransformClassToPlain | @TransformClassToPlain({ groups: ["user"] }) | Transform the method return with classToPlain and expose the properties on the class. |
@TransformClassToClass | @TransformClassToClass({ groups: ["user"] }) | Transform the method return with classToClass and expose the properties on the class. |
@TransformPlainToClass | @TransformPlainToClass(User, { groups: ["user"] }) | Transform the method return with plainToClass and expose the properties on the class. |
上面的decorator接受一个可选参数: ClassTransformOptions — 转换选项,如: groups, version, name
An example:
@Exclude()
class User {
id: number;
@Expose()
firstName: string;
@Expose()
lastName: string;
@Expose({ groups: ['user.email'] })
email: string;
password: string;
}
class UserController {
@TransformClassToPlain({ groups: ['user.email'] })
getUser() {
const user = new User();
user.firstName = 'Snir';
user.lastName = 'Segal';
user.password = 'imnosuperman';
return user;
}
}
const controller = new UserController();
const user = controller.getUser();
user
变量将只包含firstName,lastName, email属性,因为它们是
暴露变量。email属性也暴露了,因为我们将组指定为 "user.email"。
使用泛型⬆
不支持泛型,因为TypeScript还没有很好的反射能力。 一旦TypeScript团队为我们提供更好的运行时类型反射工具,泛型就会被实现。 有一些调整你可以使用,也许可以解决你的问题。 Checkout this example.
隐式类型转换⬆
NOTE 如果同时使用类验证器和类转换器,则可能不希望启用此函数。
启用基于Typescript提供的类型信息的内置类型之间的自动转换。默认情况下禁用。
import { IsString } from 'class-validator';
class MyPayload {
@IsString()
prop: string;
}
const result1 = plainToClass(MyPayload, { prop: 1234 }, { enableImplicitConversion: true });
const result2 = plainToClass(MyPayload, { prop: 1234 }, { enableImplicitConversion: false });
/**
* result1 will be `{ prop: "1234" }` - 请注意:prop值转换为了string。
* result2 will be `{ prop: 1234 }` - 默认行为
*/
它如何处理循环引用?⬆
循环引用被忽略。
例如,如果你要将包含属性photos
的类User
转换为Photo
类型,
和Photo
包含user
到其父user
的链接,那么user
将在转换过程中被忽略。
循环引用只在classToClass
有效。
在Angular2中使用⬆
假设您想加载 users
,并希望他们自动映射到User
类的实例。
import { plainToClass } from 'class-transformer';
this.http
.get('users.json')
.map(res => res.json())
.map(res => plainToClass(User, res as Object[]))
.subscribe(users => {
// 现在`users`是User[]类型,每个用户都有getName()和isAdult()方法可用
console.log(users);
});
您还可以将类ClassTransformer
作为服务注入到providers
中,并使用它的方法。
示例:如何在plunker中使用angular 2
源代码在这里。
例子⬆
查看./sample中的示例以获得更多示例 用法。
发行说明⬆
参见有关破坏更改的信息和发布说明此处。
转载自:https://juejin.cn/post/6904890590602330119