@tsed/json-mapper 8.44.3 → 8.44.4
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/lib/esm/domain/JsonDeserializer.js +17 -37
- package/lib/esm/domain/JsonMapperCompiler.js +37 -0
- package/lib/esm/domain/JsonSerializer.js +16 -29
- package/lib/types/domain/JsonDeserializer.d.ts +1 -25
- package/lib/types/domain/JsonMapperCompiler.d.ts +29 -0
- package/lib/types/domain/JsonSerializer.d.ts +1 -20
- package/package.json +5 -5
|
@@ -14,6 +14,22 @@ import { getJsonMapperTypes } from "./JsonMapperTypesContainer.js";
|
|
|
14
14
|
function hasConversionMethod(mapper, name) {
|
|
15
15
|
return isFunction(mapper[name]);
|
|
16
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* The registered function reads the mapper methods at call time, so a replaced method needs no registration.
|
|
19
|
+
*/
|
|
20
|
+
function createTypeMapperEntry(mapper, model, name) {
|
|
21
|
+
const hasConversion = hasConversionMethod(mapper, name);
|
|
22
|
+
const fn = hasConversion
|
|
23
|
+
? (value, options) => mapper[name](value, options)
|
|
24
|
+
: (value, options) => mapper.deserialize(value, { ...options, type: model });
|
|
25
|
+
return { mapper, name, hasConversion, fn };
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The function must be built again when the mapper gained or lost the method named after the type.
|
|
29
|
+
*/
|
|
30
|
+
function hasTypeMapperChanged(entry, mapper) {
|
|
31
|
+
return entry.hasConversion !== hasConversionMethod(mapper, entry.name);
|
|
32
|
+
}
|
|
17
33
|
function isDeserializable(obj, options) {
|
|
18
34
|
if ((!!options.collectionType && isNil(obj)) || obj === undefined) {
|
|
19
35
|
return false;
|
|
@@ -45,11 +61,6 @@ export class JsonDeserializer extends JsonMapperCompiler {
|
|
|
45
61
|
* @protected
|
|
46
62
|
*/
|
|
47
63
|
this.constructors = {};
|
|
48
|
-
/**
|
|
49
|
-
* The type mappers registered from the types container, with the function each one was registered with
|
|
50
|
-
* @protected
|
|
51
|
-
*/
|
|
52
|
-
this.typeMappers = new Map();
|
|
53
64
|
this.addGlobal("newInstanceOf", this.newInstanceOf.bind(this));
|
|
54
65
|
this.addGlobal("alterBeforeDeserialize", this.alterBeforeDeserialize.bind(this));
|
|
55
66
|
this.addGlobal("alterAfterDeserialize", this.alterAfterDeserialize.bind(this));
|
|
@@ -210,43 +221,12 @@ export class JsonDeserializer extends JsonMapperCompiler {
|
|
|
210
221
|
}
|
|
211
222
|
return (writer) => writer.callMapper(nestedMapper.id, varKey(key), formatOpts);
|
|
212
223
|
}
|
|
213
|
-
/**
|
|
214
|
-
* A type mapper must be registered again when it was never registered, when the mapper changed, when the mapper
|
|
215
|
-
* gained or lost the method named after the type, or when its entry was replaced or removed from the cache.
|
|
216
|
-
* The registered function reads the mapper methods at call time, so a replaced method needs no registration.
|
|
217
|
-
*/
|
|
218
|
-
isTypeMapperOutdated(model, mapper) {
|
|
219
|
-
const registered = this.typeMappers.get(model);
|
|
220
|
-
if (!registered) {
|
|
221
|
-
return true;
|
|
222
|
-
}
|
|
223
|
-
const mapperChanged = registered.mapper !== mapper;
|
|
224
|
-
const hasConversion = hasConversionMethod(mapper, registered.name);
|
|
225
|
-
const conversionChanged = registered.hasConversion !== hasConversion;
|
|
226
|
-
// two models with the same name (Date and "Date") share the mapper table entry: the last registered one wins
|
|
227
|
-
const entryReplaced = this.cache.get(model)?.get("typeMapper")?.fn !== registered.fn || this.mappers[registered.name] !== registered.fn;
|
|
228
|
-
return mapperChanged || conversionChanged || entryReplaced;
|
|
229
|
-
}
|
|
230
|
-
registerTypeMapper(model, mapper) {
|
|
231
|
-
const name = nameOf(model);
|
|
232
|
-
const hasConversion = hasConversionMethod(mapper, name);
|
|
233
|
-
const fn = hasConversion
|
|
234
|
-
? (value, options) => mapper[name](value, options)
|
|
235
|
-
: (value, options) => mapper.deserialize(value, { ...options, type: model });
|
|
236
|
-
this.addTypeMapper(model, fn);
|
|
237
|
-
this.typeMappers.set(model, { mapper, name, hasConversion, fn });
|
|
238
|
-
}
|
|
239
224
|
mapOptions({ groups, useAlias = true, types, ...options }) {
|
|
240
225
|
if (options.store instanceof JsonParameterStore) {
|
|
241
226
|
return this.mapOptions(mapParamStoreOptions(options.store, options));
|
|
242
227
|
}
|
|
243
228
|
const customMappers = {};
|
|
244
|
-
types
|
|
245
|
-
types.forEach((mapper, model) => {
|
|
246
|
-
if (model !== Array && model !== Set && model !== Map && this.isTypeMapperOutdated(model, mapper)) {
|
|
247
|
-
this.registerTypeMapper(model, mapper);
|
|
248
|
-
}
|
|
249
|
-
});
|
|
229
|
+
this.syncTypeMappers(types || getJsonMapperTypes(), createTypeMapperEntry, hasTypeMapperChanged);
|
|
250
230
|
const strictGroups = options.strictGroups ?? JsonMapperSettings.strictGroups;
|
|
251
231
|
return {
|
|
252
232
|
...options,
|
|
@@ -17,6 +17,11 @@ export class JsonMapperCompiler {
|
|
|
17
17
|
* @protected
|
|
18
18
|
*/
|
|
19
19
|
this.types = new Map();
|
|
20
|
+
/**
|
|
21
|
+
* The type mappers registered from the types container, with the function each one was registered with
|
|
22
|
+
* @protected
|
|
23
|
+
*/
|
|
24
|
+
this.typeMappers = new Map();
|
|
20
25
|
/**
|
|
21
26
|
* Cached executable mappers by his id
|
|
22
27
|
* @protected
|
|
@@ -124,6 +129,38 @@ export class JsonMapperCompiler {
|
|
|
124
129
|
}
|
|
125
130
|
return storeGroups.get(groupsId);
|
|
126
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Register the type mappers of the types container (Array, Set and Map excepted: the compiler maps them itself).
|
|
134
|
+
* A type mapper is registered again only when it is outdated, so `map()` does not rebuild them on every call.
|
|
135
|
+
*
|
|
136
|
+
* @param types The type mappers by model.
|
|
137
|
+
* @param create Build the entry, and the function, a type mapper is registered with.
|
|
138
|
+
* @param hasChanged Tell whether the mapper changed since its entry was built, beyond being replaced by another mapper.
|
|
139
|
+
*/
|
|
140
|
+
syncTypeMappers(types, create, hasChanged) {
|
|
141
|
+
types.forEach((mapper, model) => {
|
|
142
|
+
if (model !== Array && model !== Set && model !== Map && this.isTypeMapperOutdated(model, mapper, hasChanged)) {
|
|
143
|
+
const entry = create(mapper, model, nameOf(model));
|
|
144
|
+
this.addTypeMapper(model, entry.fn);
|
|
145
|
+
this.typeMappers.set(model, entry);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* A type mapper must be registered again when it was never registered, when the mapper was replaced or changed,
|
|
151
|
+
* or when its entry was replaced or removed from the cache.
|
|
152
|
+
*/
|
|
153
|
+
isTypeMapperOutdated(model, mapper, hasChanged) {
|
|
154
|
+
const registered = this.typeMappers.get(model);
|
|
155
|
+
if (!registered) {
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
return (registered.mapper !== mapper ||
|
|
159
|
+
hasChanged(registered, mapper) ||
|
|
160
|
+
// two models with the same name (Date and "Date") share the mapper table entry: the last registered one wins
|
|
161
|
+
this.cache.get(model)?.get("typeMapper")?.fn !== registered.fn ||
|
|
162
|
+
this.mappers[registered.name] !== registered.fn);
|
|
163
|
+
}
|
|
127
164
|
execMapper(id, value, options) {
|
|
128
165
|
if (isObjectID(value)) {
|
|
129
166
|
return value.toString();
|
|
@@ -55,6 +55,21 @@ function createTypeMapper(mapper, model, name) {
|
|
|
55
55
|
type: model
|
|
56
56
|
});
|
|
57
57
|
}
|
|
58
|
+
function createTypeMapperEntry(mapper, model, name) {
|
|
59
|
+
return {
|
|
60
|
+
mapper,
|
|
61
|
+
name,
|
|
62
|
+
serialize: mapper.serialize,
|
|
63
|
+
conversion: mapper[name],
|
|
64
|
+
fn: createTypeMapper(mapper, model, name)
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The function of a type mapper is built from its `serialize` and conversion methods: it must be built again when one changed.
|
|
69
|
+
*/
|
|
70
|
+
function hasTypeMapperChanged(entry, mapper) {
|
|
71
|
+
return entry.serialize !== mapper.serialize || entry.conversion !== mapper[entry.name];
|
|
72
|
+
}
|
|
58
73
|
/**
|
|
59
74
|
* Serializes data structures according to the metadata captured by `@tsed/schema`.
|
|
60
75
|
*
|
|
@@ -64,11 +79,6 @@ function createTypeMapper(mapper, model, name) {
|
|
|
64
79
|
export class JsonSerializer extends JsonMapperCompiler {
|
|
65
80
|
constructor() {
|
|
66
81
|
super();
|
|
67
|
-
/**
|
|
68
|
-
* The type mappers registered from the types container, with the function each one was registered with
|
|
69
|
-
* @protected
|
|
70
|
-
*/
|
|
71
|
-
this.typeMappers = new Map();
|
|
72
82
|
this.addTypeMapper(Object, this.mapObject.bind(this));
|
|
73
83
|
this.addTypeMapper(Array, this.mapArray.bind(this));
|
|
74
84
|
this.addTypeMapper(Map, this.mapMap.bind(this));
|
|
@@ -117,32 +127,9 @@ export class JsonSerializer extends JsonMapperCompiler {
|
|
|
117
127
|
writer.add(this.mapAdditionalProperties(entity, properties));
|
|
118
128
|
return writer.return("obj").root().toString();
|
|
119
129
|
}
|
|
120
|
-
/**
|
|
121
|
-
* A type mapper must be registered again when it was never registered, when the mapper or one of the
|
|
122
|
-
* methods its function was built from changed, or when its entry was replaced or removed from the cache.
|
|
123
|
-
*/
|
|
124
|
-
isTypeMapperOutdated(model, mapper) {
|
|
125
|
-
const registered = this.typeMappers.get(model);
|
|
126
|
-
if (!registered) {
|
|
127
|
-
return true;
|
|
128
|
-
}
|
|
129
|
-
const mapperChanged = registered.mapper !== mapper;
|
|
130
|
-
const methodsChanged = registered.serialize !== mapper.serialize || registered.conversion !== mapper[registered.name];
|
|
131
|
-
// two models with the same name (Date and "Date") share the mapper table entry: the last registered one wins
|
|
132
|
-
const entryReplaced = this.cache.get(model)?.get("typeMapper")?.fn !== registered.fn || this.mappers[registered.name] !== registered.fn;
|
|
133
|
-
return mapperChanged || methodsChanged || entryReplaced;
|
|
134
|
-
}
|
|
135
130
|
mapOptions({ groups, useAlias = true, types, ...options }) {
|
|
136
131
|
const customMappers = {};
|
|
137
|
-
types
|
|
138
|
-
types.forEach((mapper, model) => {
|
|
139
|
-
if (![Array, Set, Map].includes(model) && this.isTypeMapperOutdated(model, mapper)) {
|
|
140
|
-
const name = nameOf(model);
|
|
141
|
-
const fn = createTypeMapper(mapper, model, name);
|
|
142
|
-
this.addTypeMapper(model, fn);
|
|
143
|
-
this.typeMappers.set(model, { mapper, name, serialize: mapper.serialize, conversion: mapper[name], fn });
|
|
144
|
-
}
|
|
145
|
-
});
|
|
132
|
+
this.syncTypeMappers(types || getJsonMapperTypes(), createTypeMapperEntry, hasTypeMapperChanged);
|
|
146
133
|
const strictGroups = options.strictGroups ?? JsonMapperSettings.strictGroups;
|
|
147
134
|
return {
|
|
148
135
|
...options,
|
|
@@ -1,17 +1,6 @@
|
|
|
1
|
-
import { CachedGroupsJsonMapper, CachedJsonMapper,
|
|
1
|
+
import { CachedGroupsJsonMapper, CachedJsonMapper, JsonMapperCompiler } from "./JsonMapperCompiler.js";
|
|
2
2
|
import { Type } from "@tsed/core";
|
|
3
|
-
import { JsonMapperMethods } from "../interfaces/JsonMapperMethods.js";
|
|
4
3
|
import { JsonDeserializerOptions } from "./JsonDeserializerOptions.js";
|
|
5
|
-
/**
|
|
6
|
-
* A registered type mapper. `hasConversion` tells whether the mapper had a method named after the type
|
|
7
|
-
* (e.g. `PrimitiveMapper.String`) when it was registered: the function calls that method, otherwise `deserialize()`.
|
|
8
|
-
*/
|
|
9
|
-
type TypeMapperEntry = {
|
|
10
|
-
mapper: JsonMapperMethods;
|
|
11
|
-
name: string;
|
|
12
|
-
hasConversion: boolean;
|
|
13
|
-
fn: JsonMapperCallback<any>;
|
|
14
|
-
};
|
|
15
4
|
/**
|
|
16
5
|
* Deserializes raw JSON data into class instances based on schema metadata,
|
|
17
6
|
* handling collections, discriminators, hooks, and registered type mappers.
|
|
@@ -22,11 +11,6 @@ export declare class JsonDeserializer extends JsonMapperCompiler<JsonDeserialize
|
|
|
22
11
|
* @protected
|
|
23
12
|
*/
|
|
24
13
|
protected constructors: Record<string, Type<any>>;
|
|
25
|
-
/**
|
|
26
|
-
* The type mappers registered from the types container, with the function each one was registered with
|
|
27
|
-
* @protected
|
|
28
|
-
*/
|
|
29
|
-
protected typeMappers: Map<any, TypeMapperEntry>;
|
|
30
14
|
constructor();
|
|
31
15
|
map<Model = any>(input: any, options?: JsonDeserializerOptions): Model;
|
|
32
16
|
compile(model: Type<any>, groups: false | string[]): CachedJsonMapper<JsonDeserializerOptions>;
|
|
@@ -42,13 +26,6 @@ export declare class JsonDeserializer extends JsonMapperCompiler<JsonDeserialize
|
|
|
42
26
|
private mapDiscriminator;
|
|
43
27
|
private mapProperty;
|
|
44
28
|
private getPropertyFiller;
|
|
45
|
-
/**
|
|
46
|
-
* A type mapper must be registered again when it was never registered, when the mapper changed, when the mapper
|
|
47
|
-
* gained or lost the method named after the type, or when its entry was replaced or removed from the cache.
|
|
48
|
-
* The registered function reads the mapper methods at call time, so a replaced method needs no registration.
|
|
49
|
-
*/
|
|
50
|
-
private isTypeMapperOutdated;
|
|
51
|
-
private registerTypeMapper;
|
|
52
29
|
private mapOptions;
|
|
53
30
|
private mapAdditionalProperties;
|
|
54
31
|
private mapObject;
|
|
@@ -59,4 +36,3 @@ export declare class JsonDeserializer extends JsonMapperCompiler<JsonDeserialize
|
|
|
59
36
|
private alterAfterDeserialize;
|
|
60
37
|
private mapGenericsOptions;
|
|
61
38
|
}
|
|
62
|
-
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Type } from "@tsed/core";
|
|
2
2
|
import { JsonSchema } from "@tsed/schema";
|
|
3
|
+
import type { JsonMapperMethods } from "../interfaces/JsonMapperMethods.js";
|
|
3
4
|
/**
|
|
4
5
|
* Signature exposed by compiled mappers. They accept an input value plus contextual options
|
|
5
6
|
* and return the serialized/deserialized result.
|
|
@@ -16,6 +17,15 @@ export type CachedJsonMapper<Options> = {
|
|
|
16
17
|
* Cached mapper registry keyed by the groups fingerprint generated for a schema.
|
|
17
18
|
*/
|
|
18
19
|
export type CachedGroupsJsonMapper<Options> = Map<string, CachedJsonMapper<Options>>;
|
|
20
|
+
/**
|
|
21
|
+
* A type mapper registered from the types container: the mapper, the name of its model and the function it was
|
|
22
|
+
* registered with. Subclasses add the values their outdated check compares.
|
|
23
|
+
*/
|
|
24
|
+
export type TypeMapperEntry = {
|
|
25
|
+
mapper: JsonMapperMethods;
|
|
26
|
+
name: string;
|
|
27
|
+
fn: JsonMapperCallback<any>;
|
|
28
|
+
};
|
|
19
29
|
/**
|
|
20
30
|
* Base compiler that turns schema metadata into executable (de)serialization functions.
|
|
21
31
|
* Subclasses supply `map`, `alterValue`, and `createMapper` implementations to specialize
|
|
@@ -33,6 +43,11 @@ export declare abstract class JsonMapperCompiler<Options extends Record<string,
|
|
|
33
43
|
* @protected
|
|
34
44
|
*/
|
|
35
45
|
protected types: Map<Type<any>, Type<any>>;
|
|
46
|
+
/**
|
|
47
|
+
* The type mappers registered from the types container, with the function each one was registered with
|
|
48
|
+
* @protected
|
|
49
|
+
*/
|
|
50
|
+
protected typeMappers: Map<any, TypeMapperEntry>;
|
|
36
51
|
/**
|
|
37
52
|
* Cached executable mappers by his id
|
|
38
53
|
* @protected
|
|
@@ -69,6 +84,20 @@ export declare abstract class JsonMapperCompiler<Options extends Record<string,
|
|
|
69
84
|
compile(model: Type<any> | string, groups: false | string[], opts?: {
|
|
70
85
|
mapper?: any;
|
|
71
86
|
}): CachedJsonMapper<Options>;
|
|
87
|
+
/**
|
|
88
|
+
* Register the type mappers of the types container (Array, Set and Map excepted: the compiler maps them itself).
|
|
89
|
+
* A type mapper is registered again only when it is outdated, so `map()` does not rebuild them on every call.
|
|
90
|
+
*
|
|
91
|
+
* @param types The type mappers by model.
|
|
92
|
+
* @param create Build the entry, and the function, a type mapper is registered with.
|
|
93
|
+
* @param hasChanged Tell whether the mapper changed since its entry was built, beyond being replaced by another mapper.
|
|
94
|
+
*/
|
|
95
|
+
protected syncTypeMappers<Entry extends TypeMapperEntry>(types: Map<Type | Symbol | string, JsonMapperMethods>, create: (mapper: JsonMapperMethods, model: any, name: string) => Entry, hasChanged: (entry: Entry, mapper: JsonMapperMethods) => boolean): void;
|
|
96
|
+
/**
|
|
97
|
+
* A type mapper must be registered again when it was never registered, when the mapper was replaced or changed,
|
|
98
|
+
* or when its entry was replaced or removed from the cache.
|
|
99
|
+
*/
|
|
100
|
+
private isTypeMapperOutdated;
|
|
72
101
|
protected execMapper(id: string, value: any, options: Options): any;
|
|
73
102
|
protected abstract map(input: any, options: Options): any;
|
|
74
103
|
protected abstract alterValue(schemaId: string, value: any, options: Options): any;
|
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
import { Type } from "@tsed/core";
|
|
2
|
-
import {
|
|
3
|
-
import { JsonMapperCompiler, JsonMapperCallback } from "./JsonMapperCompiler.js";
|
|
2
|
+
import { JsonMapperCompiler } from "./JsonMapperCompiler.js";
|
|
4
3
|
import { JsonSerializerOptions } from "./JsonSerializerOptions.js";
|
|
5
|
-
type TypeMapperEntry = {
|
|
6
|
-
mapper: JsonMapperMethods;
|
|
7
|
-
name: string;
|
|
8
|
-
serialize: any;
|
|
9
|
-
conversion: any;
|
|
10
|
-
fn: JsonMapperCallback<any>;
|
|
11
|
-
};
|
|
12
4
|
/**
|
|
13
5
|
* Serializes data structures according to the metadata captured by `@tsed/schema`.
|
|
14
6
|
*
|
|
@@ -16,20 +8,10 @@ type TypeMapperEntry = {
|
|
|
16
8
|
* registered type mappers (e.g., components in `src/components`).
|
|
17
9
|
*/
|
|
18
10
|
export declare class JsonSerializer extends JsonMapperCompiler<JsonSerializerOptions> {
|
|
19
|
-
/**
|
|
20
|
-
* The type mappers registered from the types container, with the function each one was registered with
|
|
21
|
-
* @protected
|
|
22
|
-
*/
|
|
23
|
-
protected typeMappers: Map<any, TypeMapperEntry>;
|
|
24
11
|
constructor();
|
|
25
12
|
map(input: any, options?: JsonSerializerOptions): any;
|
|
26
13
|
protected alterValue(schemaId: string, value: any, options: JsonSerializerOptions): any;
|
|
27
14
|
protected createMapper(model: Type<any>, id: string, groups: false | string[]): string;
|
|
28
|
-
/**
|
|
29
|
-
* A type mapper must be registered again when it was never registered, when the mapper or one of the
|
|
30
|
-
* methods its function was built from changed, or when its entry was replaced or removed from the cache.
|
|
31
|
-
*/
|
|
32
|
-
private isTypeMapperOutdated;
|
|
33
15
|
private mapOptions;
|
|
34
16
|
private mapProperty;
|
|
35
17
|
private getPropertyFiller;
|
|
@@ -44,4 +26,3 @@ export declare class JsonSerializer extends JsonMapperCompiler<JsonSerializerOpt
|
|
|
44
26
|
private mapItem;
|
|
45
27
|
private mapJSON;
|
|
46
28
|
}
|
|
47
|
-
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tsed/json-mapper",
|
|
3
|
-
"version": "8.44.
|
|
3
|
+
"version": "8.44.4",
|
|
4
4
|
"description": "Json mapper module for Ts.ED Framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"TypeScript",
|
|
@@ -41,10 +41,10 @@
|
|
|
41
41
|
"tslib": ">=2.7.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@tsed/barrels": "8.44.
|
|
45
|
-
"@tsed/core": "8.44.
|
|
46
|
-
"@tsed/schema": "8.44.
|
|
47
|
-
"@tsed/typescript": "8.44.
|
|
44
|
+
"@tsed/barrels": "8.44.4",
|
|
45
|
+
"@tsed/core": "8.44.4",
|
|
46
|
+
"@tsed/schema": "8.44.4",
|
|
47
|
+
"@tsed/typescript": "8.44.4",
|
|
48
48
|
"temporal-polyfill": "1.0.1",
|
|
49
49
|
"typescript": "6.0.2",
|
|
50
50
|
"vitest": "4.1.4"
|