ts-serializable 3.6.0 → 3.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -0
- package/dist/classes/Serializable.d.ts +23 -1
- package/dist/classes/Serializable.js +38 -23
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/utils/ClassToFormData.d.ts +1 -0
- package/dist/utils/ClassToFormData.js +56 -0
- package/dist/utils/GetProperyName.d.ts +2 -0
- package/dist/utils/GetProperyName.js +20 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -181,6 +181,33 @@ JSON.stringify(user);
|
|
|
181
181
|
// Result: {"firstName":"","familyName":""}
|
|
182
182
|
```
|
|
183
183
|
|
|
184
|
+
Class to FormData
|
|
185
|
+
------
|
|
186
|
+
|
|
187
|
+
Sometimes classes contain properties with the File type. Sending such classes via json is a heavy task. Converting a file property to json can freeze the interface for a few seconds if the file is large. A much better solution is to send an Ajax form. Example:
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
import { Serializable } from "ts-serializable";
|
|
191
|
+
|
|
192
|
+
export class User extends Serializable {
|
|
193
|
+
|
|
194
|
+
public firstName: string = '';
|
|
195
|
+
|
|
196
|
+
public familyName: File | null = null;
|
|
197
|
+
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ... send file function ...
|
|
201
|
+
|
|
202
|
+
await fetch("api/sendFile", {
|
|
203
|
+
method: "POST",
|
|
204
|
+
body: user.toFormData() // <- serialization class to FormData
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Naming strategies, custom names, ignoring and other decorators are supported during conversion.
|
|
210
|
+
|
|
184
211
|
Bonus
|
|
185
212
|
------
|
|
186
213
|
|
|
@@ -68,6 +68,20 @@ export declare class Serializable {
|
|
|
68
68
|
* @memberof Serializable
|
|
69
69
|
*/
|
|
70
70
|
toJSON(): Record<string, unknown>;
|
|
71
|
+
/**
|
|
72
|
+
* Serialize class to FormData.
|
|
73
|
+
*
|
|
74
|
+
* Can be used for prepare ajax form with files.
|
|
75
|
+
* Send files via ajax json its heavy task, because need convert file to base 64 format,
|
|
76
|
+
* user interface can be freeze on many seconds on this operation if file is too big.
|
|
77
|
+
* Ajax forms its lightweight alternative.
|
|
78
|
+
*
|
|
79
|
+
* @param {string} formPrefix Prefix for form property names
|
|
80
|
+
* @param {FormData} formData Can be update an existing FormData
|
|
81
|
+
* @returns {FormData}
|
|
82
|
+
* @memberof Serializable
|
|
83
|
+
*/
|
|
84
|
+
toFormData(formPrefix?: string, formData?: FormData): FormData;
|
|
71
85
|
/**
|
|
72
86
|
* Process serialization for @jsonIgnore decorator
|
|
73
87
|
*
|
|
@@ -98,5 +112,13 @@ export declare class Serializable {
|
|
|
98
112
|
* @memberof Serializable
|
|
99
113
|
*/
|
|
100
114
|
protected deserializeProperty(prop: string, acceptedTypes: AcceptedTypes[], jsonValue: unknown, settings?: Partial<SerializationSettings>): unknown;
|
|
101
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Extract correct name for property.
|
|
117
|
+
* Considers decorators for transforming the property name.
|
|
118
|
+
*
|
|
119
|
+
* @param {string} property Source name of property
|
|
120
|
+
* @param {Partial<SerializationSettings>} settings Serialization settings
|
|
121
|
+
* @returns
|
|
122
|
+
*/
|
|
123
|
+
protected getJsonPropertyName(property: string, settings?: Partial<SerializationSettings>): string;
|
|
102
124
|
}
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
/* eslint-disable max-statements */
|
|
7
7
|
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
8
8
|
import { SerializationSettings } from "../models/SerializationSettings.js";
|
|
9
|
+
import { classToFormData } from "../utils/ClassToFormData.js";
|
|
10
|
+
import { getPropertyName } from "../utils/GetProperyName.js";
|
|
9
11
|
/**
|
|
10
12
|
* Class how help you deserialize object to classes.
|
|
11
13
|
*
|
|
@@ -98,19 +100,38 @@ export class Serializable {
|
|
|
98
100
|
* @memberof Serializable
|
|
99
101
|
*/
|
|
100
102
|
toJSON() {
|
|
101
|
-
const fromJson = { ...this };
|
|
102
103
|
const toJson = {};
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
104
|
+
const keys = Reflect.ownKeys(this);
|
|
105
|
+
for (const key of keys) {
|
|
106
|
+
if (typeof key === "symbol") {
|
|
107
|
+
// eslint-disable-next-line no-continue
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (this.hasOwnProperty(key)) {
|
|
111
|
+
if (Reflect.getMetadata("ts-serializable:jsonIgnore", this.constructor.prototype, key) !== true) {
|
|
112
|
+
const toProp = this.getJsonPropertyName(key);
|
|
113
|
+
Reflect.set(toJson, toProp, Reflect.get(this, key));
|
|
109
114
|
}
|
|
110
115
|
}
|
|
111
116
|
}
|
|
112
117
|
return toJson;
|
|
113
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Serialize class to FormData.
|
|
121
|
+
*
|
|
122
|
+
* Can be used for prepare ajax form with files.
|
|
123
|
+
* Send files via ajax json its heavy task, because need convert file to base 64 format,
|
|
124
|
+
* user interface can be freeze on many seconds on this operation if file is too big.
|
|
125
|
+
* Ajax forms its lightweight alternative.
|
|
126
|
+
*
|
|
127
|
+
* @param {string} formPrefix Prefix for form property names
|
|
128
|
+
* @param {FormData} formData Can be update an existing FormData
|
|
129
|
+
* @returns {FormData}
|
|
130
|
+
* @memberof Serializable
|
|
131
|
+
*/
|
|
132
|
+
toFormData(formPrefix, formData) {
|
|
133
|
+
return classToFormData(this, formPrefix, formData);
|
|
134
|
+
}
|
|
114
135
|
/**
|
|
115
136
|
* Process serialization for @jsonIgnore decorator
|
|
116
137
|
*
|
|
@@ -227,22 +248,16 @@ export class Serializable {
|
|
|
227
248
|
this.onWrongType(prop, "is invalid", jsonValue);
|
|
228
249
|
return Reflect.get(this, prop);
|
|
229
250
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
}
|
|
241
|
-
if (Serializable.defaultSettings.namingStrategy) {
|
|
242
|
-
const { namingStrategy } = Serializable.defaultSettings;
|
|
243
|
-
return namingStrategy.toJsonName(thisProperty) ?? thisProperty;
|
|
244
|
-
}
|
|
245
|
-
return thisProperty;
|
|
251
|
+
/**
|
|
252
|
+
* Extract correct name for property.
|
|
253
|
+
* Considers decorators for transforming the property name.
|
|
254
|
+
*
|
|
255
|
+
* @param {string} property Source name of property
|
|
256
|
+
* @param {Partial<SerializationSettings>} settings Serialization settings
|
|
257
|
+
* @returns
|
|
258
|
+
*/
|
|
259
|
+
getJsonPropertyName(property, settings) {
|
|
260
|
+
return getPropertyName(this, property, settings);
|
|
246
261
|
}
|
|
247
262
|
}
|
|
248
263
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -15,3 +15,5 @@ export { SnakeCaseNamingStrategy } from "./naming-strategies/SnakeCaseNamingStra
|
|
|
15
15
|
export { PascalCaseNamingStrategy } from "./naming-strategies/PascalCaseNamingStrategy.js";
|
|
16
16
|
export { KebabCaseNamingStrategy } from "./naming-strategies/KebabCaseNamingStrategy.js";
|
|
17
17
|
export { CamelCaseNamingStrategy } from "./naming-strategies/CamelCaseNamingStrategy.js";
|
|
18
|
+
export { classToFormData } from "./utils/ClassToFormData.js";
|
|
19
|
+
export { getPropertyName } from "./utils/GetProperyName.js";
|
package/dist/index.js
CHANGED
|
@@ -18,3 +18,6 @@ export { SnakeCaseNamingStrategy } from "./naming-strategies/SnakeCaseNamingStra
|
|
|
18
18
|
export { PascalCaseNamingStrategy } from "./naming-strategies/PascalCaseNamingStrategy.js";
|
|
19
19
|
export { KebabCaseNamingStrategy } from "./naming-strategies/KebabCaseNamingStrategy.js";
|
|
20
20
|
export { CamelCaseNamingStrategy } from "./naming-strategies/CamelCaseNamingStrategy.js";
|
|
21
|
+
// Utils
|
|
22
|
+
export { classToFormData } from "./utils/ClassToFormData.js";
|
|
23
|
+
export { getPropertyName } from "./utils/GetProperyName.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const classToFormData: (obj: object, formPrefix?: string, formData?: FormData) => FormData;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { getPropertyName } from "./GetProperyName.js";
|
|
2
|
+
// eslint-disable-next-line max-statements, max-lines-per-function
|
|
3
|
+
export const classToFormData = (obj, formPrefix, formData) => {
|
|
4
|
+
const newFormData = formData ?? new FormData();
|
|
5
|
+
const keys = Reflect.ownKeys(obj);
|
|
6
|
+
for (const key of keys) {
|
|
7
|
+
if (typeof key === "symbol") {
|
|
8
|
+
// eslint-disable-next-line no-continue
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
// eslint-disable-next-line no-prototype-builtins
|
|
12
|
+
if (obj.hasOwnProperty(key)) {
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
14
|
+
if (Reflect.getMetadata("ts-serializable:jsonIgnore", obj.constructor.prototype, key) !== true) {
|
|
15
|
+
const name = formPrefix ?
|
|
16
|
+
`${formPrefix}.${getPropertyName(obj, key)}` :
|
|
17
|
+
getPropertyName(obj, key);
|
|
18
|
+
/*
|
|
19
|
+
* The function is defined inside the function to capture variables and
|
|
20
|
+
* solve the problem of order of definition.
|
|
21
|
+
*/
|
|
22
|
+
const processValue = (value, index) => {
|
|
23
|
+
if (Array.isArray(value)) {
|
|
24
|
+
for (const [oneIndex, oneVal] of value.entries()) {
|
|
25
|
+
processValue(oneVal, oneIndex);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
else if (value === null) {
|
|
29
|
+
// Null is not sent in the form.
|
|
30
|
+
}
|
|
31
|
+
else if (value instanceof File) {
|
|
32
|
+
newFormData.append(name, value);
|
|
33
|
+
}
|
|
34
|
+
else if (value instanceof Date) {
|
|
35
|
+
newFormData.append(name, value.toISOString());
|
|
36
|
+
}
|
|
37
|
+
else if (typeof value === "object") {
|
|
38
|
+
let prefix = name;
|
|
39
|
+
// For arrays of objects in form need add index
|
|
40
|
+
if (typeof index === "number") {
|
|
41
|
+
prefix += `[${index.toString()}]`;
|
|
42
|
+
}
|
|
43
|
+
classToFormData(value, prefix, formData);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
47
|
+
newFormData.append(name, String(value));
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const uValue = Reflect.get(obj, key);
|
|
51
|
+
processValue(uValue);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return newFormData;
|
|
56
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
2
|
+
import { Serializable } from "../classes/Serializable.js";
|
|
3
|
+
// eslint-disable-next-line max-statements
|
|
4
|
+
export const getPropertyName = (obj, property, settings) => {
|
|
5
|
+
if (Reflect.hasMetadata("ts-serializable:jsonName", obj.constructor.prototype, property)) {
|
|
6
|
+
return Reflect.getMetadata("ts-serializable:jsonName", obj.constructor.prototype, property);
|
|
7
|
+
}
|
|
8
|
+
if (settings?.namingStrategy) {
|
|
9
|
+
return settings.namingStrategy.toJsonName(property);
|
|
10
|
+
}
|
|
11
|
+
if (Reflect.hasMetadata("ts-serializable:jsonObject", obj.constructor)) {
|
|
12
|
+
const objectSettings = Reflect.getMetadata("ts-serializable:jsonObject", obj.constructor);
|
|
13
|
+
return objectSettings.namingStrategy?.toJsonName(property) ?? property;
|
|
14
|
+
}
|
|
15
|
+
if (Serializable.defaultSettings.namingStrategy) {
|
|
16
|
+
const { namingStrategy } = Serializable.defaultSettings;
|
|
17
|
+
return namingStrategy.toJsonName(property);
|
|
18
|
+
}
|
|
19
|
+
return property;
|
|
20
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-serializable",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.2",
|
|
4
4
|
"author": "Eugene Labutin",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/LabEG/Serializable#readme",
|
|
@@ -23,9 +23,10 @@
|
|
|
23
23
|
"scripts": {
|
|
24
24
|
"lint": "eslint --fix ./src/ ./tests/",
|
|
25
25
|
"test": "node --import ./ts-loader.js --test --test-reporter=spec --test-reporter-destination=stdout \"tests/**/*.spec.ts\"",
|
|
26
|
+
"test-watch": "node --watch --import ./ts-loader.js --test --test-reporter=spec --test-reporter-destination=stdout \"tests/**/*.spec.ts\"",
|
|
26
27
|
"coverage": "node --import ./ts-loader.js --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=coverage/lcov.info \"tests/**/*.spec.ts\"",
|
|
27
28
|
"build": "tsc --project tsconfig.build.json && node ./dist/index.js",
|
|
28
|
-
"prepublishOnly": "npm run lint && npm run build && npm run test",
|
|
29
|
+
"prepublishOnly": "npm run lint && npm run build && npm run test && node ./dist/index.js",
|
|
29
30
|
"release": "cliff-jumper --name 'ts-serializable' --package-path '.' --no-skip-changelog --no-skip-tag",
|
|
30
31
|
"prepare": "husky install"
|
|
31
32
|
},
|