UNPKG

18.3 kBMarkdownView Raw
1# class-transformer
2
3[![Build Status](https://travis-ci.org/pleerock/class-transformer.svg?branch=master)](https://travis-ci.org/pleerock/class-transformer)
4[![codecov](https://codecov.io/gh/pleerock/class-transformer/branch/master/graph/badge.svg)](https://codecov.io/gh/pleerock/class-transformer)
5[![npm version](https://badge.fury.io/js/class-transformer.svg)](https://badge.fury.io/js/class-transformer)
6[![Dependency Status](https://david-dm.org/pleerock/class-transformer.svg)](https://david-dm.org/pleerock/class-transformer)
7[![devDependency Status](https://david-dm.org/pleerock/class-transformer/dev-status.svg)](https://david-dm.org/pleerock/class-transformer#info=devDependencies)
8[![Join the chat at https://gitter.im/pleerock/class-transformer](https://badges.gitter.im/pleerock/class-transformer.svg)](https://gitter.im/pleerock/class-transformer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
9
10Its ES6 and Typescript era. Nowadays you are working with classes and constructor objects more then ever.
11Class-transformer allows you to transform plain object to some instance of class and versa.
12Also it allows to serialize / deserialize object based on criteria.
13This tool is super useful on both frontend and backend.
14
15Example how to use with angular 2 in [plunker](http://plnkr.co/edit/Mja1ZYAjVySWASMHVB9R).
16Source code is available [here](https://github.com/pleerock/class-transformer-demo).
17
18## What is class-transformer
19
20In JavaScript there are two types of objects:
21
22* plain (literal) objects
23* class (constructor) objects
24
25Plain objects are objects that are instances of `Object` class.
26Sometimes they are called **literal** objects, when created via `{}` notation.
27Class objects are instances of classes with own defined constructor, properties and methods.
28Usually you define them via `class` notation.
29
30So, what is the problem?
31
32Sometimes you want to transform plain javascript object to the ES6 **classes** you have.
33For example, if you are loading a json from your backend, some api or from a json file,
34and after you `JSON.parse` it you have a plain javascript object, not instance of class you have.
35
36For example you have a list of users in your `users.json` that you are loading:
37
38```json
39[{
40 "id": 1,
41 "firstName": "Johny",
42 "lastName": "Cage",
43 "age": 27
44},
45{
46 "id": 2,
47 "firstName": "Ismoil",
48 "lastName": "Somoni",
49 "age": 50
50},
51{
52 "id": 3,
53 "firstName": "Luke",
54 "lastName": "Dacascos",
55 "age": 12
56}]
57```
58And you have a `User` class:
59
60```javascript
61export class User {
62 id: number;
63 firstName: string;
64 lastName: string;
65 age: number;
66
67 getName() {
68 return this.firstName + " " + this.lastName;
69 }
70
71 isAdult() {
72 return this.age > 36 && this.age < 60;
73 }
74}
75```
76
77You are assuming that you are downloading users of type `User` from `users.json` file and may want to write
78following code:
79
80```javascript
81fetch("users.json").then((users: User[]) => {
82 // you can use users here, and type hinting also will be available to you,
83 // but users are not actually instances of User class
84 // this means that you can't use methods of User class
85});
86```
87
88In this code you can use `users[0].id`, you can also use `users[0].firstName` and `users[0].lastName`.
89However you cannot use `users[0].getName()` or `users[0].isAdult()` because "users" actually is
90array of plain javascript objects, not instances of User object.
91You actually lied to compiler when you said that its `users: User[]`.
92
93So what to do? How to make a `users` array of instances of `User` objects instead of plain javascript objects?
94Solution is to create new instances of User object and manually copy all properties to new objects.
95But things may go wrong very fast once you have a more complex object hierarchy.
96
97Alternatives? Yes, you can use class-transformer. Purpose of this library is to help you to map you plain javascript
98objects to the instances of classes you have.
99
100This library also great for models exposed in your APIs,
101because it provides a great tooling to control what your models are exposing in your API.
102Here is example how it will look like:
103
104```javascript
105fetch("users.json").then((users: Object[]) => {
106 const realUsers = plainToClass(users);
107 // now each user in realUsers is instance of User class
108});
109```
110
111Now you can use `users[0].getName()` and `users[0].isAdult()` methods.
112
113## Installation
114
115### Node.js
116
1171. Install module:
118
119 `npm install class-transformer --save`
120
1212. `reflect-metadata` shim is required, install it too:
122
123 `npm install reflect-metadata --save`
124
125 and make sure to import it in a global place, like app.ts:
126
127 ```javascript
128 import "reflect-metadata";
129 ```
130
1313. ES6 features are used, if you are using old version of node.js you may need to install es6-shim:
132
133 `npm install es6-shim --save`
134
135 and import it in a global place like app.ts:
136
137 ```javascript
138 import "es6-shim";
139 ```
140
141### Browser
142
1431. Install module:
144
145 `npm install class-transformer --save`
146
1472. `reflect-metadata` shim is required, install it too:
148
149 `npm install reflect-metadata --save`
150
151 add `<script>` to reflect-metadata in the head of your `index.html`:
152
153 ```html
154 <html>
155 <head>
156 <!-- ... -->
157 <script src="node_modules/reflect-metadata/Reflect.js"></script>
158 </head>
159 <!-- ... -->
160 </html>
161 ```
162
163 If you are using angular 2 you should already have this shim installed.
164
1653. If you are using system.js you may want to add this into `map` and `package` config:
166
167 ```json
168 {
169 "map": {
170 "class-transformer": "node_modules/class-transformer"
171 },
172 "packages": {
173 "class-transformer": { "main": "index.js", "defaultExtension": "js" }
174 }
175 }
176 ```
177
178## Methods
179
180#### plainToClass
181
182This method transforms a plain javascript object to instance of specific class.
183
184```javascript
185import {plainToClass} from "class-transformer";
186
187let users = plainToClass(User, userJson); // to convert user plain object a single user. also supports arrays
188```
189
190#### classToPlain
191
192This method transforms your class object back to plain javascript object, that can be `JSON.stringify` later.
193
194```javascript
195import {classToPlain} from "class-transformer";
196let photo = classToPlain(photo);
197```
198
199#### classToClass
200
201This method transforms your class object into new instance of the class object.
202This maybe treated as deep clone of your objects.
203
204```javascript
205import {classToClass} from "class-transformer";
206let photo = classToClass(photo);
207```
208
209You can also use a `ignoreDecorators` option in transformation options to ignore all decorators you classes is using.
210
211#### serialize
212
213You can serialize your model right to the json using `serialize` method:
214
215```javascript
216import {serialize} from "class-transformer";
217let photo = serialize(photo);
218```
219
220`serialize` works with both arrays and non-arrays.
221
222#### deserialize and deserializeArray
223
224You can deserialize your model to from a json using `deserialize` method:
225
226```javascript
227import {deserialize} from "class-transformer";
228let photo = deserialize(photo);
229```
230
231To make deserialization to work with arrays use `deserializeArray` method:
232
233```javascript
234import {deserializeArray} from "class-transformer";
235let photos = deserializeArray(photos);
236```
237
238## Working with nested objects
239
240When you are trying to transform objects that have nested objects,
241its required to known what type of object you are trying to transform.
242Since Typescript does not have good reflection abilities yet,
243we should implicitly specify what type of object each property contain.
244This is done using `@Type` decorator.
245
246Lets say we have an album with photos.
247And we are trying to convert album plain object to class object:
248
249```javascript
250import {Type, plainToClass} from "class-transformer";
251
252export class Album {
253
254 id: number;
255
256 name: string;
257
258 @Type(() => Photo)
259 photos: Photo[];
260}
261
262export class Photo {
263 id: number;
264 filename: string;
265}
266
267let album = plainToClass(Album, albumJson);
268// now album is Album object with Photo objects inside
269```
270
271## Exposing getters and method return values
272
273You can expose what your getter or method return by setting a `@Expose()` decorator to those getters or methods:
274
275```javascript
276import {Expose} from "class-transformer";
277
278export class User {
279
280 id: number;
281 firstName: string;
282 lastName: string;
283 password: string;
284
285 @Expose()
286 get name() {
287 return this.firstName + " " + this.lastName;
288 }
289
290 @Expose()
291 getFullName() {
292 return this.firstName + " " + this.lastName;
293 }
294}
295```
296
297## Exposing properties with different names
298
299If you want to expose some of properties with a different name,
300you can do it by specifying a `name` option to `@Expose` decorator:
301
302```javascript
303import {Expose} from "class-transformer";
304
305export class User {
306
307 @Expose("uid")
308 id: number;
309
310 firstName: string;
311
312 lastName: string;
313
314 @Expose("secretKey")
315 password: string;
316
317 @Expose("fullName")
318 getFullName() {
319 return this.firstName + " " + this.lastName;
320 }
321}
322```
323
324## Skipping specific properties
325
326Sometimes you want to skip some properties during transformation.
327This can be done using `@Exclude` decorator:
328
329```javascript
330import {Exclude} from "class-transformer";
331
332export class User {
333
334 id: number;
335
336 email: string;
337
338 @Exclude()
339 password: string;
340}
341```
342
343Now when you transform a User, `password` property will be skipped and not be included in the transformed result.
344
345## Skipping depend of operation
346
347You can control on what operation you will exclude a property. Use `toClassOnly` or `toPlainOnly` options:
348
349```javascript
350import {Exclude} from "class-transformer";
351
352export class User {
353
354 id: number;
355
356 email: string;
357
358 @Exclude({ toPlainOnly: true })
359 password: string;
360}
361```
362
363Now `password` property will be excluded only during `classToPlain` operation. Oppositely, use `toClassOnly` option.
364
365## Skipping all properties of the class
366
367You can skip all properties of the class, and expose only those are needed explicitly:
368
369```javascript
370import {Exclude, Expose} from "class-transformer";
371
372@Exclude()
373export class User {
374
375 @Expose()
376 id: number;
377
378 @Expose()
379 email: string;
380
381 password: string;
382}
383```
384
385Now `id` and `email` will be exposed, and password will be excluded during transformation.
386Alternatively, you can set exclusion strategy during transformation:
387
388```javascript
389import {classToPlain} from "class-transformer";
390let photo = classToPlain(photo, { strategy: "excludeAll" });
391```
392
393In this case you don't need to `@Exclude()` a whole class.
394
395## Skipping private properties, or some prefixed properties
396
397If you name your private properties with a prefix, lets say with `_`,
398then you can exclude such properties from transformation too:
399
400```javascript
401import {classToPlain} from "class-transformer";
402let photo = classToPlain(photo, { excludePrefixes: ["_"] });
403```
404
405This will skip all properties that start with `_` prefix.
406You can pass any number of prefixes and all properties that begin with these prefixes will be ignored.
407For example:
408
409```javascript
410import {Expose} from "class-transformer";
411
412export class User {
413
414 id: number;
415 private _firstName: string;
416 private _lastName: string;
417 _password: string;
418
419 setName(firstName: string, lastName: string) {
420 this._firstName = firstName;
421 this._lastName = lastName;
422 }
423
424 @Expose()
425 get name() {
426 return this.firstName + " " + this.lastName;
427 }
428
429}
430
431const user = new User();
432user.id = 1;
433user.setName("Johny", "Cage");
434user._password = 123;
435
436const plainUser = classToPlain(user, { excludePrefixes: ["_"] });
437// here plainUser will be equal to
438// { id: 1, name: "Johny Cage" }
439```
440
441## Using groups to control excluded properties
442
443You can use groups to control what data will be exposed and what will not be:
444
445```javascript
446import {Exclude, Expose} from "class-transformer";
447
448@Exclude()
449export class User {
450
451 id: number;
452
453 name: string;
454
455 @Expose({ groups: ["user", "admin"] }) // this means that this data will be exposed only to users and admins
456 email: string;
457
458 @Expose({ groups: ["user"] }) // this means that this data will be exposed only to users
459 password: string;
460}
461```
462
463```javascript
464import {classToPlain} from "class-transformer";
465let user1 = classToPlain(user, { groups: ["user"] }); // will contain id, name, email and password
466let user2 = classToPlain(user, { groups: ["admin"] }); // will contain id, name and email
467```
468
469## Using versioning to control exposed and excluded properties
470
471If you are building an API that has different versions, class-transformer has extremely useful tools for that.
472You can control which properties of your model should be exposed or excluded in what version. Example:
473
474```javascript
475import {Exclude, Expose} from "class-transformer";
476
477@Exclude()
478export class User {
479
480 id: number;
481
482 name: string;
483
484 @Expose({ since: 0.7, until: 1 }) // this means that this property will be exposed for version starting from 0.7 until 1
485 email: string;
486
487 @Expose({ since: 2.1 }) // this means that this property will be exposed for version starting from 2.1
488 password: string;
489}
490```
491
492```javascript
493import {classToPlain} from "class-transformer";
494let user1 = classToPlain(user, { version: 0.5 }); // will contain id and name
495let user2 = classToPlain(user, { version: 0.7 }); // will contain id, name and email
496let user3 = classToPlain(user, { version: 1 }); // will contain id and name
497let user4 = classToPlain(user, { version: 2 }); // will contain id and name
498let user5 = classToPlain(user, { version: 2.1 }); // will contain id, name nad password
499```
500
501## Сonverting date strings into Date objects
502
503Sometimes you have a Date in your plain javascript object received in a string format.
504And you want to create a real javascript Date object from it.
505You can do it simply by passing a Date object to the `@Type` decorator:
506
507```javascript
508import {Type} from "class-transformer";
509
510export class User {
511
512 id: number;
513
514 email: string;
515
516 password: string;
517
518 @Type(() => Date)
519 registrationDate: Date;
520}
521```
522
523Note, that dates will be converted to strings when you'll try to convert class object to plain object.
524
525Same technique can be used with `Number`, `String`, `Boolean`
526primitive types when you want to convert your values into these types.
527
528## Working with arrays
529
530When you are using arrays you must provide a type of the object that array contains.
531This type, you specify in a `@Type()` decorator:
532
533```javascript
534import {ArrayType} from "class-transformer";
535
536export class Photo {
537
538 id: number;
539
540 name: string;
541
542 @Type(() => Album)
543 albums: Album[];
544}
545```
546
547You can also use custom array types:
548
549```javascript
550import {ArrayType} from "class-transformer";
551
552export class AlbumCollection extends Array<Album> {
553 // custom array functions ...
554}
555
556export class Photo {
557
558 id: number;
559
560 name: string;
561
562 @Type(() => Album)
563 albums: AlbumCollection;
564}
565```
566
567Library will handle proper transformation automatically.
568
569## Additional data transformation
570
571You can perform additional data transformation using `@Transform` decorator.
572For example, you want to make your `Date` object to be a `moment` object when you are
573transforming object from plain to class:
574
575```javascript
576import {Transform} from "class-transformer";
577import * as moment from "moment";
578import {Moment} from "moment";
579
580export class Photo {
581
582 id: number;
583
584 @Transform(value => moment(value), { toClassOnly: true })
585 date: Moment;
586}
587```
588
589Now when you call `plainToClass` and send a plain representation of the Photo object,
590it will convert a date value in your photo object to moment date.
591`@Transform` decorator also supports groups and versioning.
592
593## Working with generics
594
595Generics are not supported because TypeScript does not have good reflection abilities yet.
596Once TypeScript team provide us better runtime type reelection tools, generics will be implemented.
597There are some tweaks however you can use, that maybe can solve your problem.
598[Checkout this example.](https://github.com/pleerock/class-transformer/tree/master/sample/sample4-generics)
599
600## How does it handle circular references?
601
602Circular references are ignored.
603For example, if you are transforming class `User` that contains property `photos` with type of `Photo`,
604 and `Photo` contains link `user` to its parent `User`, then `user` will be ignored during transformation.
605Circular references are not ignored only during `classToClass` operation.
606
607## Example with Angular2
608
609Lets say you want to download users and want them automatically to be mapped to the instances of `User` class.
610
611```javascript
612import {plainToClass} from "class-transformer";
613
614this.http
615 .get("users.json")
616 .map(res => res.json())
617 .map(res => plainToClass(User, res as Object[]))
618 .subscribe(users => {
619 // now "users" is type of User[] and each user have getName() and isAdult() methods available
620 console.log(users);
621 });
622```
623
624You can also inject a class `ClassTransformer` as a service in `providers`, and use its methods.
625
626Example how to use with angular 2 in [plunker](http://plnkr.co/edit/Mja1ZYAjVySWASMHVB9R).
627Source code is [here](https://github.com/pleerock/class-transformer-demo).
628
629## Samples
630
631Take a look on samples in [./sample](https://github.com/pleerock/class-transformer/tree/master/sample) for more examples of
632usages.
633
634## Release notes
635
636See information about breaking changes and release notes [here](https://github.com/pleerock/class-transformer/tree/master/doc/release-notes.md).