@tsed/json-mapper 8.44.11 → 8.44.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.
|
@@ -39,6 +39,9 @@ function isDeserializable(obj, options) {
|
|
|
39
39
|
function varKey(k) {
|
|
40
40
|
return `__${k}`;
|
|
41
41
|
}
|
|
42
|
+
function ignoredKey(k) {
|
|
43
|
+
return `__${k}_ignored`;
|
|
44
|
+
}
|
|
42
45
|
function mapParamStoreOptions(store, options) {
|
|
43
46
|
return {
|
|
44
47
|
...options,
|
|
@@ -110,6 +113,7 @@ export class JsonDeserializer extends JsonMapperCompiler {
|
|
|
110
113
|
createMapper(model, id, groups) {
|
|
111
114
|
const entity = s.store(model);
|
|
112
115
|
const properties = new Set();
|
|
116
|
+
const fallbacks = new Map();
|
|
113
117
|
const schemaProperties = [...getPropertiesStores(entity).values()];
|
|
114
118
|
const writer = new Writer().arrow("input", "options");
|
|
115
119
|
writer.if("isNil(input)").return("input");
|
|
@@ -126,17 +130,26 @@ export class JsonDeserializer extends JsonMapperCompiler {
|
|
|
126
130
|
// properties
|
|
127
131
|
writer.add(...schemaProperties.flatMap((propertyStore) => {
|
|
128
132
|
const key = propertyStore.propertyName;
|
|
133
|
+
const alias = String(propertyStore.parent.schema.getAliasOf(key) || key);
|
|
129
134
|
// TODO V8 add this line: properties.add(key as string);
|
|
130
|
-
properties.add(
|
|
135
|
+
properties.add(alias);
|
|
131
136
|
if ((propertyStore.schema?.$ignore && isBoolean(propertyStore.schema?.$ignore)) ||
|
|
132
137
|
propertyStore.isGetterOnly() ||
|
|
133
138
|
(propertyStore.schema?.$hooks?.has("groups") && this.alterGroups(propertyStore.schema, groups))) {
|
|
139
|
+
// an excluded property must not be filled from its declared name either
|
|
140
|
+
properties.add(key);
|
|
134
141
|
return;
|
|
135
142
|
}
|
|
143
|
+
// the declared name of an aliased property is still read as an additional property (fallback when the
|
|
144
|
+
// property mapped nothing), but it must not overwrite the value the property mapped
|
|
145
|
+
if (alias !== key) {
|
|
146
|
+
const mappedNothing = `${varKey(key)} === undefined`;
|
|
147
|
+
fallbacks.set(key, propertyStore.schema?.$hooks?.has("ignore") ? `!${ignoredKey(key)} && ${mappedNothing}` : mappedNothing);
|
|
148
|
+
}
|
|
136
149
|
return this.mapProperty(propertyStore, id, groups);
|
|
137
150
|
}));
|
|
138
151
|
// additional properties
|
|
139
|
-
writer.add(this.mapAdditionalProperties(entity, properties, groups));
|
|
152
|
+
writer.add(this.mapAdditionalProperties(entity, properties, fallbacks, groups));
|
|
140
153
|
// post hook
|
|
141
154
|
if (entity.schema.$hooks?.has("afterDeserialize")) {
|
|
142
155
|
this.schemes[id] = entity.schema;
|
|
@@ -165,10 +178,15 @@ export class JsonDeserializer extends JsonMapperCompiler {
|
|
|
165
178
|
const formatOpts = format && `options: {format: '${format}'}`;
|
|
166
179
|
let writer = new Writer().add(`// Map ${key} ${id} ${groups || ""}`);
|
|
167
180
|
const pick = key !== aliasKey ? `options.useAlias ? '${aliasKey}' : '${key}'` : `'${key}'`;
|
|
181
|
+
let declare = `let ${varKey(key)}`;
|
|
168
182
|
// ignore hook (deprecated)
|
|
169
183
|
if (propertyStore.schema?.$hooks?.has("ignore")) {
|
|
170
184
|
this.schemes[schemaId] = propertyStore.schema;
|
|
171
|
-
|
|
185
|
+
// declared outside the branch: the additional properties loop reads them
|
|
186
|
+
writer.add(`${declare};`);
|
|
187
|
+
declare = varKey(key);
|
|
188
|
+
writer.const(ignoredKey(key), `alterIgnore('${schemaId}', {...options, self: input})`);
|
|
189
|
+
writer = writer.if(`!${ignoredKey(key)}`);
|
|
172
190
|
}
|
|
173
191
|
// pre hook
|
|
174
192
|
const hasDeserializer = propertyStore.schema?.$hooks?.has("onDeserialize");
|
|
@@ -178,7 +196,7 @@ export class JsonDeserializer extends JsonMapperCompiler {
|
|
|
178
196
|
const opts = Writer.options(formatOpts);
|
|
179
197
|
getter = `alterValue('${schemaId}', input[${pick}], ${opts})`;
|
|
180
198
|
}
|
|
181
|
-
const ifWriter = writer.set(
|
|
199
|
+
const ifWriter = writer.set(declare, getter).if(`${varKey(key)} !== undefined`);
|
|
182
200
|
const fill = this.getPropertyFiller(propertyStore, id, groups, formatOpts);
|
|
183
201
|
if (hasDeserializer) {
|
|
184
202
|
fill(ifWriter.if(`${varKey(key)} === input.${key}`));
|
|
@@ -238,14 +256,29 @@ export class JsonDeserializer extends JsonMapperCompiler {
|
|
|
238
256
|
generics: options.generics || {}
|
|
239
257
|
};
|
|
240
258
|
}
|
|
241
|
-
|
|
259
|
+
/**
|
|
260
|
+
* @param properties The keys never copied: the keys declared by the model (aliases included), and the declared names of
|
|
261
|
+
* the excluded properties.
|
|
262
|
+
* @param fallbacks The condition to copy the declared name, by declared name of the aliased properties: the property
|
|
263
|
+
* was not ignored and mapped nothing (no input value, or a hook returned `undefined`). A declared name is copied only
|
|
264
|
+
* when the alias is used and this condition is true. The mapped value is checked, not the instance, which may hold a
|
|
265
|
+
* field initializer.
|
|
266
|
+
*/
|
|
267
|
+
mapAdditionalProperties(entity, properties, fallbacks, groups) {
|
|
242
268
|
const additionalProperties = entity.schema.get("additionalProperties");
|
|
243
|
-
const exclude = [...properties.values()].map((key) => `'${key}'`).join(", ");
|
|
244
269
|
const writer = new Writer();
|
|
245
270
|
writer.add("// add additional properties");
|
|
246
|
-
|
|
247
|
-
if
|
|
248
|
-
|
|
271
|
+
// dynamic additional properties option: the keys are read only when it is enabled, it is disabled by default
|
|
272
|
+
const scope = additionalProperties ? writer : writer.if("options.additionalProperties");
|
|
273
|
+
let each = scope.each("objectKeys(input)", ["key"]);
|
|
274
|
+
if (properties.size) {
|
|
275
|
+
each = each.if(Writer.notOneOf("key", properties));
|
|
276
|
+
}
|
|
277
|
+
if (additionalProperties && fallbacks.size) {
|
|
278
|
+
const conditions = Array.from(fallbacks, ([name, fallback]) => {
|
|
279
|
+
return `(key !== '${name}' || (options.useAlias && ${fallback}))`;
|
|
280
|
+
});
|
|
281
|
+
each = each.if(conditions.join(" && "));
|
|
249
282
|
}
|
|
250
283
|
if (additionalProperties && additionalProperties instanceof JsonSchema) {
|
|
251
284
|
const nestedMapper = this.compile(additionalProperties.class, groups);
|
|
@@ -256,8 +289,7 @@ export class JsonDeserializer extends JsonMapperCompiler {
|
|
|
256
289
|
each.set("obj[key]", "input[key]");
|
|
257
290
|
return writer;
|
|
258
291
|
}
|
|
259
|
-
|
|
260
|
-
each.if(`options.additionalProperties && obj[key] === undefined`).set("obj[key]", "input[key]");
|
|
292
|
+
each.if("obj[key] === undefined").set("obj[key]", "input[key]");
|
|
261
293
|
return writer;
|
|
262
294
|
}
|
|
263
295
|
mapObject(input, options) {
|
|
@@ -236,10 +236,9 @@ export class JsonSerializer extends JsonMapperCompiler {
|
|
|
236
236
|
mapAdditionalProperties(entity, properties) {
|
|
237
237
|
const additionalProperties = !!entity.schema.get("additionalProperties");
|
|
238
238
|
if (additionalProperties) {
|
|
239
|
-
const exclude = [...properties.values()].map((key) => `'${key}'`).join(", ");
|
|
240
239
|
const writer = new Writer();
|
|
241
240
|
writer.add("// add additional properties");
|
|
242
|
-
writer.each("objectKeys(input)", ["key"]).if(
|
|
241
|
+
writer.each("objectKeys(input)", ["key"]).if(Writer.notOneOf("key", properties)).set("obj[key]", "input[key]");
|
|
243
242
|
return writer;
|
|
244
243
|
}
|
|
245
244
|
}
|
package/lib/esm/domain/Writer.js
CHANGED
|
@@ -22,6 +22,13 @@ export class Writer {
|
|
|
22
22
|
static mapperFrom(key, options) {
|
|
23
23
|
return `execMapper(nameOf(classOf(${key})), ${key}, ${options})`;
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Condition true when `key` is none of the given values. It compares the key to each value instead of
|
|
27
|
+
* calling `[...].includes(key)`, which allocates an array for every key it checks.
|
|
28
|
+
*/
|
|
29
|
+
static notOneOf(key, values) {
|
|
30
|
+
return Array.from(values, (value) => `${key} !== '${value}'`).join(" && ");
|
|
31
|
+
}
|
|
25
32
|
static options(...args) {
|
|
26
33
|
args = args.filter(Boolean);
|
|
27
34
|
return !args.length ? "options" : Writer.object.assign("...options", ...args);
|
|
@@ -27,6 +27,14 @@ export declare class JsonDeserializer extends JsonMapperCompiler<JsonDeserialize
|
|
|
27
27
|
private mapProperty;
|
|
28
28
|
private getPropertyFiller;
|
|
29
29
|
private mapOptions;
|
|
30
|
+
/**
|
|
31
|
+
* @param properties The keys never copied: the keys declared by the model (aliases included), and the declared names of
|
|
32
|
+
* the excluded properties.
|
|
33
|
+
* @param fallbacks The condition to copy the declared name, by declared name of the aliased properties: the property
|
|
34
|
+
* was not ignored and mapped nothing (no input value, or a hook returned `undefined`). A declared name is copied only
|
|
35
|
+
* when the alias is used and this condition is true. The mapped value is checked, not the instance, which may hold a
|
|
36
|
+
* field initializer.
|
|
37
|
+
*/
|
|
30
38
|
private mapAdditionalProperties;
|
|
31
39
|
private mapObject;
|
|
32
40
|
private mapSet;
|
|
@@ -13,6 +13,11 @@ export declare class Writer {
|
|
|
13
13
|
static indent(lines: string[]): string[];
|
|
14
14
|
static mapper(mapperId: string, key: string, options: string): string;
|
|
15
15
|
static mapperFrom(key: string, options: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Condition true when `key` is none of the given values. It compares the key to each value instead of
|
|
18
|
+
* calling `[...].includes(key)`, which allocates an array for every key it checks.
|
|
19
|
+
*/
|
|
20
|
+
static notOneOf(key: string, values: Iterable<string>): string;
|
|
16
21
|
static options(...args: string[]): string;
|
|
17
22
|
add(...lines: (undefined | string | Writer)[]): this;
|
|
18
23
|
arrow(...args: string[]): Writer;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tsed/json-mapper",
|
|
3
|
-
"version": "8.44.
|
|
3
|
+
"version": "8.44.12",
|
|
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.12",
|
|
45
|
+
"@tsed/core": "8.44.12",
|
|
46
|
+
"@tsed/schema": "8.44.12",
|
|
47
|
+
"@tsed/typescript": "8.44.12",
|
|
48
48
|
"temporal-polyfill": "1.0.1",
|
|
49
49
|
"typescript": "6.0.2",
|
|
50
50
|
"vitest": "4.1.4"
|