ts-serializable 3.0.8 → 3.0.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-serializable",
3
- "version": "3.0.8",
3
+ "version": "3.0.12",
4
4
  "description": "Serialization and deserializtion for classes",
5
5
  "main": "./dist/index.js",
6
6
  "type": "module",
@@ -10,7 +10,7 @@
10
10
  "reflect-metadata": ">=0.1.0"
11
11
  },
12
12
  "devDependencies": {
13
- "@labeg/code-style": "^2.0.52",
13
+ "@labeg/code-style": "^2.0.58",
14
14
  "@types/chai": "^4.3.4",
15
15
  "@types/mocha": "^10.0.0",
16
16
  "chai": "^4.3.7",
@@ -1,271 +0,0 @@
1
- /* eslint-disable no-prototype-builtins */
2
- /* eslint-disable @typescript-eslint/no-unnecessary-condition */
3
- /* eslint-disable complexity */
4
- /* eslint-disable max-lines-per-function */
5
- /* eslint-disable max-statements */
6
- /* eslint-disable @typescript-eslint/no-unsafe-argument */
7
-
8
- import type {AcceptedTypes} from "../models/AcceptedType.js";
9
- import {SerializationSettings} from "../models/SerializationSettings.js";
10
-
11
- /**
12
- * Class how help you deserialize object to classes.
13
- *
14
- * @export
15
- * @class Serializable
16
- */
17
- export class Serializable {
18
-
19
- /**
20
- * Global setting for serialization and deserialization
21
- *
22
- * @static
23
- * @type {SerializationSettings}
24
- * @memberof Serializable
25
- */
26
- public static defaultSettings: SerializationSettings = new SerializationSettings();
27
-
28
- /**
29
- * Deserialize object from static method.
30
- *
31
- * Example:
32
- * const obj: MyObject = MyObject.fromJSON({...data});
33
- *
34
- * @static
35
- * @param {object} json
36
- * @returns {object}
37
- * @memberof Serializable
38
- */
39
- public static fromJSON<T extends Serializable>(
40
- this: new () => T,
41
- json: object,
42
- settings?: Partial<SerializationSettings>
43
- ): T {
44
- return new this().fromJSON(json, settings);
45
- }
46
-
47
- /**
48
- * Fill property of current model by data from json.
49
- *
50
- * Example:
51
- * const obj: MyObject = new MyObject().fromJSON({...data});
52
- *
53
- * @param {object} ujson
54
- * @returns {this}
55
- * @memberof Serializable
56
- */
57
- public fromJSON (json: object, settings?: Partial<SerializationSettings>): this {
58
- const unknownJson: unknown = json;
59
-
60
- if (
61
- unknownJson === null ||
62
- Array.isArray(unknownJson) ||
63
- typeof unknownJson !== "object"
64
- ) {
65
- this.onWrongType(String(unknownJson), "is not object", unknownJson);
66
- return this;
67
- }
68
-
69
- // eslint-disable-next-line guard-for-in
70
- for (const thisProp in this) {
71
- // Naming strategy and jsonName decorator
72
- let jsonProp: string = this.getJsonPropertyName(thisProp, settings);
73
-
74
- // For deep copy
75
- if (!unknownJson?.hasOwnProperty(jsonProp) && unknownJson?.hasOwnProperty(thisProp)) {
76
- jsonProp = thisProp;
77
- }
78
-
79
- if (
80
- unknownJson?.hasOwnProperty(jsonProp) &&
81
- this.hasOwnProperty(thisProp) &&
82
- Reflect.hasMetadata("ts-serializable:jsonTypes", this.constructor.prototype, thisProp)
83
- ) {
84
- const acceptedTypes: AcceptedTypes[] = Reflect.getMetadata(
85
- "ts-serializable:jsonTypes",
86
- this.constructor.prototype,
87
- thisProp
88
- ) as [];
89
- const jsonValue: unknown = Reflect.get(unknownJson, jsonProp) as unknown;
90
- const extractedValue = this.deserializeProperty(thisProp, acceptedTypes, jsonValue, settings);
91
- Reflect.set(this, thisProp, extractedValue);
92
- }
93
- }
94
-
95
- return this;
96
- }
97
-
98
- /**
99
- * Process serialization for @jsonIgnore decorator
100
- *
101
- * @returns {object}
102
- * @memberof Serializable
103
- */
104
- public toJSON (): Record<string, unknown> {
105
- const fromJson: this = {...this};
106
- const toJson: Record<string, unknown> = {};
107
-
108
- for (const prop in fromJson) {
109
- // Json.hasOwnProperty(prop) - preserve for deserialization for other classes with methods
110
- if (fromJson.hasOwnProperty(prop) && this.hasOwnProperty(prop)) {
111
- if (Reflect.getMetadata("ts-serializable:jsonIgnore", this.constructor.prototype, prop) !== true) {
112
- const toProp = this.getJsonPropertyName(prop);
113
- Reflect.set(toJson, toProp, Reflect.get(fromJson, prop));
114
- }
115
- }
116
- }
117
-
118
- return toJson;
119
- }
120
-
121
- /**
122
- * Process exceptions from wrong types.
123
- * By default just print warning in console, but can by override for drop exception or logging to backend.
124
- *
125
- * @protected
126
- * @param {string} prop
127
- * @param {string} message
128
- * @param {(unknown)} jsonValue
129
- * @memberof Serializable
130
- */
131
- protected onWrongType (prop: string, message: string, jsonValue: unknown): void {
132
- // eslint-disable-next-line no-console
133
- console.error(`${this.constructor.name}.fromJSON: json.${prop} ${message}:`, jsonValue);
134
- }
135
-
136
- /**
137
- * //todo: write jsdoc
138
- *
139
- * @private
140
- * @param {object} object
141
- * @param {string} prop
142
- * @param {AcceptedTypes[]} acceptedTypes
143
- * @param {(unknown)} jsonValue
144
- * @returns {(Object | null | void)}
145
- * @memberof Serializable
146
- */
147
- protected deserializeProperty (
148
- prop: string,
149
- acceptedTypes: AcceptedTypes[],
150
- jsonValue: unknown,
151
- settings?: Partial<SerializationSettings>
152
- ): unknown {
153
- for (const acceptedType of acceptedTypes) { // Type Symbol is not a property
154
- if (// Null
155
- acceptedType === null &&
156
- jsonValue === null
157
- ) {
158
- return null;
159
- } else if (// Void, for deep copy classes only, json don't have void type
160
- acceptedType === void 0 &&
161
- jsonValue === void 0
162
- ) {
163
- return void 0;
164
- } else if (// Boolean, Boolean
165
- acceptedType === Boolean &&
166
- (typeof jsonValue === "boolean" || jsonValue instanceof Boolean)
167
- ) {
168
- return Boolean(jsonValue);
169
- } else if (// Number, Number
170
- acceptedType === Number &&
171
- (typeof jsonValue === "number" || jsonValue instanceof Number)
172
- ) {
173
- return Number(jsonValue);
174
- } else if (// String, String
175
- acceptedType === String &&
176
- (typeof jsonValue === "string" || jsonValue instanceof String)
177
- ) {
178
- return String(jsonValue);
179
- } else if (// Object, Object
180
- acceptedType === Object &&
181
- (typeof jsonValue === "object")
182
- ) {
183
- return Object(jsonValue);
184
- } else if (// Date
185
- acceptedType === Date &&
186
- (typeof jsonValue === "string" || jsonValue instanceof String || jsonValue instanceof Date)
187
- ) {
188
- // 0 year, 0 month, 0 days, 0 hours, 0 minutes, 0 seconds
189
- let unicodeTime: number = new Date("0000-01-01T00:00:00.000").getTime();
190
-
191
- if (typeof jsonValue === "string") {
192
- unicodeTime = Date.parse(jsonValue);
193
- } else if (jsonValue instanceof String) {
194
- unicodeTime = Date.parse(String(jsonValue));
195
- } else if (jsonValue instanceof Date) {
196
- unicodeTime = jsonValue.getTime();
197
- }
198
- if (isNaN(unicodeTime)) { // Preserve invalid time
199
- this.onWrongType(prop, "is invalid date", jsonValue);
200
- }
201
-
202
- return new Date(unicodeTime);
203
- } else if (// Array
204
- Array.isArray(acceptedType) &&
205
- Array.isArray(jsonValue)
206
- ) {
207
- if (acceptedType[0] === void 0) {
208
- this.onWrongType(prop, "invalid type", jsonValue);
209
- }
210
-
211
- return jsonValue.map((arrayValue: unknown) => this.deserializeProperty(
212
- prop,
213
- acceptedType,
214
- arrayValue,
215
- settings
216
- ));
217
- } else if (// Serializable
218
- acceptedType !== null &&
219
- acceptedType !== void 0 &&
220
- !Array.isArray(acceptedType) &&
221
- (
222
- acceptedType.prototype instanceof Serializable ||
223
- Boolean(Reflect.getMetadata("ts-serializable:jsonObjectExtended", acceptedType))
224
- ) &&
225
- jsonValue !== null &&
226
- jsonValue !== void 0 &&
227
- typeof jsonValue === "object" && !Array.isArray(jsonValue)
228
- ) {
229
- const TypeConstructor: new () => Serializable = acceptedType as new () => Serializable;
230
-
231
- return new TypeConstructor().fromJSON(jsonValue, settings);
232
- } else if (// Instance any other class, not Serializable, for parse from other classes instance
233
- acceptedType instanceof Function &&
234
- jsonValue instanceof acceptedType
235
- ) {
236
- return jsonValue;
237
- }
238
- }
239
-
240
- // Process wrong type and return default value
241
- this.onWrongType(prop, "is invalid", jsonValue);
242
-
243
- return Reflect.get(this, prop);
244
- }
245
-
246
- protected getJsonPropertyName (thisProperty: string, settings?: Partial<SerializationSettings>): string {
247
- if (Reflect.hasMetadata("ts-serializable:jsonName", this.constructor.prototype, thisProperty)) {
248
- return Reflect.getMetadata("ts-serializable:jsonName", this.constructor.prototype, thisProperty) as string;
249
- }
250
-
251
- if (settings?.namingStrategy) {
252
- return settings.namingStrategy.toJsonName(thisProperty);
253
- }
254
-
255
- if (Reflect.hasMetadata("ts-serializable:jsonObject", this.constructor)) {
256
- const objectSettings: Partial<SerializationSettings> = Reflect.getMetadata(
257
- "ts-serializable:jsonObject",
258
- this.constructor
259
- ) as Partial<SerializationSettings>;
260
- return objectSettings.namingStrategy?.toJsonName(thisProperty) ?? thisProperty;
261
- }
262
-
263
- if (Serializable.defaultSettings.namingStrategy) {
264
- const {namingStrategy} = Serializable.defaultSettings;
265
- return namingStrategy.toJsonName(thisProperty) ?? thisProperty;
266
- }
267
-
268
- return thisProperty;
269
- }
270
-
271
- }
@@ -1,6 +0,0 @@
1
- export const jsonIgnore = (): PropertyDecorator => (
2
- target: object,
3
- propertyKey: string | symbol
4
- ): void => {
5
- Reflect.defineMetadata("ts-serializable:jsonIgnore", true, target, propertyKey);
6
- };
@@ -1,6 +0,0 @@
1
- export const jsonName = (jsonPropertyName: string): PropertyDecorator => (
2
- target: object,
3
- propertyKey: string | symbol
4
- ): void => {
5
- Reflect.defineMetadata("ts-serializable:jsonName", jsonPropertyName, target, propertyKey);
6
- };
@@ -1,29 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- /* eslint-disable @typescript-eslint/no-unsafe-argument */
3
- /* eslint-disable @typescript-eslint/no-unsafe-member-access */
4
- /* eslint-disable @typescript-eslint/unbound-method */
5
- /* eslint-disable max-statements */
6
- import type {SerializationSettings} from "../models/SerializationSettings.js";
7
- import {Serializable} from "../classes/Serializable.js";
8
-
9
- export const jsonObject = (
10
- settings?: Partial<SerializationSettings>,
11
- extend?: boolean
12
- ): ClassDecorator => (target: object): void => {
13
- if (extend === true) {
14
- Reflect.set(target, "defaultSettings", Serializable.defaultSettings);
15
- Reflect.set(target, "fromJSON", Serializable.fromJSON);
16
-
17
- Reflect.set((target as any).prototype, "fromJSON", (Serializable.prototype as any).fromJSON);
18
- Reflect.set((target as any).prototype, "deserializeProperty", (Serializable.prototype as any).deserializeProperty);
19
- Reflect.set((target as any).prototype, "getJsonPropertyName", (Serializable.prototype as any).getJsonPropertyName);
20
- Reflect.set((target as any).prototype, "onWrongType", (Serializable.prototype as any).onWrongType);
21
- Reflect.set((target as any).prototype, "toJSON", (Serializable.prototype as any).toJSON);
22
-
23
- Reflect.defineMetadata("ts-serializable:jsonObjectExtended", true, target);
24
- }
25
-
26
- if (settings) {
27
- Reflect.defineMetadata("ts-serializable:jsonObject", settings, target);
28
- }
29
- };
@@ -1,8 +0,0 @@
1
- import type {AcceptedTypes} from "./../models/AcceptedType.js";
2
-
3
- export const jsonProperty = (...args: AcceptedTypes[]): PropertyDecorator => (
4
- target: object,
5
- propertyKey: string | symbol
6
- ): void => {
7
- Reflect.defineMetadata("ts-serializable:jsonTypes", args, target, propertyKey);
8
- };
@@ -1,4 +0,0 @@
1
- export enum DateFormatHandling {
2
- IsoDateFormat = 0,
3
- MicrosoftDateFormat = 1 // Not supported yet
4
- }
@@ -1,6 +0,0 @@
1
- export enum DefaultValueHandling {
2
- Include = 0,
3
- Ignore = 1, // Not supported yet
4
- Populate = 2, // Not supported yet
5
- IgnoreAndPopulate = 3 // Not supported yet
6
- }
@@ -1,9 +0,0 @@
1
- export enum LogLevels {
2
- Trace = 0,
3
- Debug = 1,
4
- Information = 2,
5
- Warning = 3,
6
- Error = 4,
7
- Critical = 5,
8
- None = 6
9
- }
@@ -1,4 +0,0 @@
1
- export enum MissingMemberHandling {
2
- Ignore = 0,
3
- Error = 1 // Not supported yet
4
- }
@@ -1,4 +0,0 @@
1
- export enum NullValueHandling {
2
- Include = 0,
3
- Ignore = 1 // Not supported yet
4
- }
@@ -1,5 +0,0 @@
1
- export enum ReferenceLoopHandling {
2
- Error = 0,
3
- Ignore = 1,
4
- Serialize = 2
5
- }
package/src/index.ts DELETED
@@ -1,25 +0,0 @@
1
- // Decoratore
2
- export {jsonIgnore} from "./decorators/JsonIgnore.js";
3
- export {jsonName} from "./decorators/JsonName.js";
4
- export {jsonObject} from "./decorators/JsonObject.js";
5
- export {jsonProperty} from "./decorators/JsonProperty.js";
6
-
7
- // Base class
8
- export {Serializable} from "./classes/Serializable.js";
9
-
10
- // Enums
11
- export {DateFormatHandling} from "./enums/DateFormatHandling.js";
12
- export {DefaultValueHandling} from "./enums/DefaultValueHandling.js";
13
- export {MissingMemberHandling} from "./enums/MissingMemberHandling.js";
14
- export {NullValueHandling} from "./enums/NullValueHandling.js";
15
- export {ReferenceLoopHandling} from "./enums/ReferenceLoopHandling.js";
16
- export {LogLevels} from "./enums/LogLevels.js";
17
-
18
- // Settings
19
- export {SerializationSettings} from "./models/SerializationSettings.js";
20
-
21
- // Naming strategies
22
- export type {INamingStrategy} from "./naming-strategies/INamingStrategy.js";
23
- export {SnakeCaseNamingStrategy} from "./naming-strategies/SnakeCaseNamingStrategy.js";
24
- export {PascalCaseNamingStrategy} from "./naming-strategies/PascalCaseNamingStrategy.js";
25
- export {KebabCaseNamingStrategy} from "./naming-strategies/KebabCaseNamingStrategy.js";
@@ -1,22 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-invalid-void-type */
2
- /* eslint-disable @typescript-eslint/no-type-alias */
3
-
4
- export type AcceptedType =
5
- BooleanConstructor |
6
- DateConstructor |
7
- NumberConstructor |
8
- ObjectConstructor |
9
- StringConstructor |
10
-
11
- // Extended deserialization
12
- SymbolConstructor |
13
- (new (...args: unknown[]) => object) |
14
- null |
15
- void;
16
- // Add ArrayBufferConstructor, MapConstructor, RegExpConstructor and many others...
17
-
18
- // eslint-disable-next-line @typescript-eslint/no-empty-interface
19
- interface IRecursiveArray<T> extends Array<IRecursiveArray<T> | T> {
20
- }
21
-
22
- export type AcceptedTypes = AcceptedType | IRecursiveArray<AcceptedType>;
@@ -1,27 +0,0 @@
1
-
2
- import {LogLevels} from "../enums/LogLevels.js";
3
- import {DefaultValueHandling} from "../enums/DefaultValueHandling.js";
4
- import {NullValueHandling} from "../enums/NullValueHandling.js";
5
- import {ReferenceLoopHandling} from "../enums/ReferenceLoopHandling.js";
6
- import {MissingMemberHandling} from "../enums/MissingMemberHandling.js";
7
- import {DateFormatHandling} from "../enums/DateFormatHandling.js";
8
- import type {INamingStrategy} from "../naming-strategies/INamingStrategy.js";
9
-
10
- // From newtonsoft https://www.newtonsoft.com/json/help/html/SerializationSettings.htm
11
- export class SerializationSettings {
12
-
13
- public dateFormatHandling: DateFormatHandling = DateFormatHandling.IsoDateFormat;
14
-
15
- public missingMemberHandling: MissingMemberHandling = MissingMemberHandling.Ignore;
16
-
17
- public referenceLoopHandling: ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
18
-
19
- public nullValueHandling: NullValueHandling = NullValueHandling.Include;
20
-
21
- public defaultValueHandling: DefaultValueHandling = DefaultValueHandling.Ignore;
22
-
23
- public namingStrategy: INamingStrategy | null = null;
24
-
25
- public logLevel: LogLevels = LogLevels.Warning;
26
-
27
- }
@@ -1,6 +0,0 @@
1
- export interface INamingStrategy {
2
-
3
- fromJsonName: (name: string) => string;
4
- toJsonName: (name: string) => string;
5
-
6
- }
@@ -1,19 +0,0 @@
1
- import type {INamingStrategy} from "./INamingStrategy.js";
2
-
3
- export class KebabCaseNamingStrategy implements INamingStrategy {
4
-
5
- public fromJsonName (name: string): string {
6
- return name.replace(
7
- /-\w/gu,
8
- (group: string) => group[1].toUpperCase()
9
- );
10
- }
11
-
12
- public toJsonName (name: string): string {
13
- return name
14
- .split(/(?=[A-Z])/u)
15
- .join("-")
16
- .toLowerCase();
17
- }
18
-
19
- }
@@ -1,13 +0,0 @@
1
- import type {INamingStrategy} from "./INamingStrategy.js";
2
-
3
- export class PascalCaseNamingStrategy implements INamingStrategy {
4
-
5
- public fromJsonName (name: string): string {
6
- return name.slice(0, 1).toLowerCase() + name.slice(1, name.length);
7
- }
8
-
9
- public toJsonName (name: string): string {
10
- return name.slice(0, 1).toUpperCase() + name.slice(1, name.length);
11
- }
12
-
13
- }
@@ -1,19 +0,0 @@
1
- import type {INamingStrategy} from "./INamingStrategy.js";
2
-
3
- export class SnakeCaseNamingStrategy implements INamingStrategy {
4
-
5
- public fromJsonName (name: string): string {
6
- return name.replace(
7
- /_\w/gu,
8
- (group: string) => group[1].toUpperCase()
9
- );
10
- }
11
-
12
- public toJsonName (name: string): string {
13
- return name
14
- .split(/(?=[A-Z])/u)
15
- .join("_")
16
- .toLowerCase();
17
- }
18
-
19
- }