json2typescript
Advanced tools
Comparing version 1.5.0-rc.0 to 1.5.0
@@ -10,3 +10,3 @@ /** | ||
* | ||
* @param target the class identifier or the class | ||
* @param classIdentifier the class identifier | ||
* | ||
@@ -17,3 +17,3 @@ * @returns | ||
*/ | ||
export declare function JsonObject(target: string | any): (target: any) => void; | ||
export declare function JsonObject(classIdentifier: string): (target: any) => void; | ||
/** | ||
@@ -20,0 +20,0 @@ * Decorator of a class property that comes from a JSON object. |
@@ -8,2 +8,6 @@ "use strict"; | ||
/** | ||
* Map of all registered json objects | ||
*/ | ||
var jsonObjectsMap = new Map(); | ||
/** | ||
* Decorator of a class that is a custom converter. | ||
@@ -20,3 +24,3 @@ * | ||
* | ||
* @param target the class identifier or the class | ||
* @param classIdentifier the class identifier | ||
* | ||
@@ -27,6 +31,16 @@ * @returns | ||
*/ | ||
function JsonObject(target) { | ||
// target is the constructor or the custom class name | ||
var classIdentifier = ""; | ||
var decorator = function (target) { | ||
function JsonObject(classIdentifier) { | ||
return function (target) { | ||
var _a; | ||
// Store the classIdentifier with the actual class reference | ||
if (jsonObjectsMap.has(classIdentifier)) { | ||
throw new Error("Fatal error in JsonConvert. " + | ||
"You must use unique class identifiers in the @JsonObject() decorator.\n\n" + | ||
"\tClass identifier: \n" + | ||
"\t\t" + classIdentifier + "\n\n" + | ||
"This class identifier has been already used for class \"" + ((_a = jsonObjectsMap.get(classIdentifier)) === null || _a === void 0 ? void 0 : _a.name) + "\".\n\n"); | ||
} | ||
else { | ||
jsonObjectsMap.set(classIdentifier, target); | ||
} | ||
target.prototype[json_convert_options_1.Settings.CLASS_IDENTIFIER] = classIdentifier.length > 0 ? classIdentifier : target.name; | ||
@@ -48,18 +62,2 @@ var mapping = target.prototype[json_convert_options_1.Settings.MAPPING_PROPERTY]; | ||
}; | ||
var type = typeof target; | ||
switch (type) { | ||
// Decorator was @JsonObject(classId) | ||
case "string": | ||
classIdentifier = target; | ||
return decorator; | ||
// Decorator was @JsonObject | ||
// Decorator was @JsonObject() | ||
// Decorator was @JsonObject(123) | ||
case "function": | ||
case "undefined": | ||
default: | ||
throw new Error("Fatal error in JsonConvert. " + | ||
"It is mandatory to pass a string as parameter in the @JsonObject decorator.\n\n" + | ||
"Use @JsonObject(classId) where classId is a string.\n\n"); | ||
} | ||
} | ||
@@ -66,0 +64,0 @@ exports.JsonObject = JsonObject; |
@@ -260,4 +260,4 @@ import { PropertyConvertingMode } from "./json-convert-enums"; | ||
/** | ||
* Determines all classes which should use the discriminator feature. | ||
* Only classes provided here can be enriched with the discriminator property. | ||
* Determines all classes which should use the lazy-loading or discriminator feature. | ||
* Only classes provided here can be used with lazy-loading or the discriminator property. | ||
* | ||
@@ -268,4 +268,4 @@ * @see https://www.npmjs.com/package/json2typescript full documentation | ||
/* | ||
* Determines all classes which should use the discriminator feature. | ||
* Only classes provided here can be enriched with the discriminator property. | ||
* Determines all classes which should use the lazy-loading or discriminator feature. | ||
* Only classes provided here can be used with lazy-loading or the discriminator property. | ||
* | ||
@@ -388,5 +388,5 @@ * @see https://www.npmjs.com/package/json2typescript full documentation | ||
*/ | ||
deserialize<T extends object>(json: object | object[], classReference: { | ||
deserialize<T extends object>(json: object | object[], classReference?: { | ||
new (): T; | ||
}): T | T[]; | ||
} | null): T | T[]; | ||
/** | ||
@@ -404,5 +404,5 @@ * Tries to deserialize a JSON object to a TypeScript object. | ||
*/ | ||
deserializeObject<T extends object>(jsonObject: any, classReference: { | ||
deserializeObject<T extends object>(jsonObject: any, classReference?: { | ||
new (): T; | ||
}): T; | ||
} | null): T; | ||
/** | ||
@@ -420,6 +420,15 @@ * Tries to deserialize a JSON array to a TypeScript array. | ||
*/ | ||
deserializeArray<T extends object>(jsonArray: any[], classReference: { | ||
deserializeArray<T extends object>(jsonArray: any[], classReference?: { | ||
new (): T; | ||
}): T[]; | ||
} | null): T[]; | ||
/** | ||
* Returns the correct class reference for the provided JSON object. | ||
* If the provided class reference is null, the class reference is retrieved from the class map using the discriminator property. | ||
* | ||
* @param jsonObject the JSON object | ||
* @param classReference the class reference | ||
* @throws throws an Error in case of failure | ||
*/ | ||
private getRealClassReference; | ||
/** | ||
* Tries to find the JSON mapping for a given class property from the given instance used for mapping, | ||
@@ -426,0 +435,0 @@ * and finally assign the value from the given dataObject |
@@ -111,4 +111,4 @@ "use strict"; | ||
/** | ||
* Determines all classes which should use the discriminator feature. | ||
* Only classes provided here can be enriched with the discriminator property. | ||
* Determines all classes which should use the lazy-loading or discriminator feature. | ||
* Only classes provided here can be used with lazy-loading or the discriminator property. | ||
* | ||
@@ -384,4 +384,4 @@ * @see https://www.npmjs.com/package/json2typescript full documentation | ||
/** | ||
* Determines all classes which should use the discriminator feature. | ||
* Only classes provided here can be enriched with the discriminator property. | ||
* Determines all classes which should use the lazy-loading or discriminator feature. | ||
* Only classes provided here can be used with lazy-loading or the discriminator property. | ||
* | ||
@@ -639,2 +639,3 @@ * @see https://www.npmjs.com/package/json2typescript full documentation | ||
JsonConvert.prototype.deserialize = function (json, classReference) { | ||
if (classReference === void 0) { classReference = null; } | ||
if (this.operationMode === json_convert_enums_1.OperationMode.DISABLE) { | ||
@@ -669,5 +670,7 @@ return json; | ||
JsonConvert.prototype.deserializeObject = function (jsonObject, classReference) { | ||
if (classReference === void 0) { classReference = null; } | ||
if (this.operationMode === json_convert_enums_1.OperationMode.DISABLE) { | ||
return jsonObject; | ||
} | ||
var realClassReference = this.getRealClassReference(jsonObject, classReference); | ||
jsonObject = this.mapUndefinedToNull && jsonObject === undefined ? null : jsonObject; | ||
@@ -702,3 +705,3 @@ // Check if the passed type is allowed | ||
} | ||
var instance = new classReference(); | ||
var instance = new realClassReference(); | ||
// Loop through all initialized class properties | ||
@@ -739,2 +742,3 @@ for (var _i = 0, _a = Object.keys(instance); _i < _a.length; _i++) { | ||
JsonConvert.prototype.deserializeArray = function (jsonArray, classReference) { | ||
if (classReference === void 0) { classReference = null; } | ||
if (this.operationMode === json_convert_enums_1.OperationMode.DISABLE) { | ||
@@ -789,2 +793,45 @@ return jsonArray; | ||
/** | ||
* Returns the correct class reference for the provided JSON object. | ||
* If the provided class reference is null, the class reference is retrieved from the class map using the discriminator property. | ||
* | ||
* @param jsonObject the JSON object | ||
* @param classReference the class reference | ||
* @throws throws an Error in case of failure | ||
*/ | ||
JsonConvert.prototype.getRealClassReference = function (jsonObject, classReference) { | ||
var _a; | ||
// First determine if the discriminator is used or not | ||
if (this.useDiscriminator) { | ||
// Check if we find the $type property. If not, throw an error. | ||
if (jsonObject.hasOwnProperty(this.discriminatorPropertyName)) { | ||
var discriminatorValue = (_a = jsonObject[this.discriminatorPropertyName]) !== null && _a !== void 0 ? _a : ""; | ||
var classReferenceNameFromMap = this.classes.get(discriminatorValue); | ||
if (classReferenceNameFromMap !== undefined && classReferenceNameFromMap !== null) { | ||
return classReferenceNameFromMap; | ||
} | ||
else { | ||
throw new Error("Fatal error in JsonConvert. " + | ||
"Discriminator value \"" + discriminatorValue + "\" could not be found in the registered classes. " + | ||
"Make sure you register the class using the method JsonConvert.registerClasses(" + discriminatorValue + ")" + | ||
"\n"); | ||
} | ||
} | ||
else { | ||
throw new Error("Fatal error in JsonConvert. " + | ||
"Discriminator property \"" + this.discriminatorPropertyName + "\" is missing in JSON object." + | ||
"\n"); | ||
} | ||
} | ||
else { | ||
// Make sure the class reference is given for if the discriminator is disabled | ||
if (classReference === null) { | ||
throw new Error("Fatal error in JsonConvert. " + | ||
"Passed parameter classReference in JsonConvert.deserialize() is null. " + | ||
"This is only allowed if discriminator feature is enabled." + | ||
"\n"); | ||
} | ||
return classReference; | ||
} | ||
}; | ||
/** | ||
* Tries to find the JSON mapping for a given class property from the given instance used for mapping, | ||
@@ -1038,3 +1085,11 @@ * and finally assign the value from the given dataObject | ||
// Check if objects match | ||
if (expectedType instanceof Object && value instanceof Object) { | ||
if ((expectedType instanceof Object || typeof expectedType === "string") && value instanceof Object) { | ||
// If the expected type is a string (means: lazy-loading), get the real type from the registered classes | ||
if (typeof expectedType === "string") { | ||
var realExpectedType = this.classes.get(expectedType); | ||
if (!realExpectedType) { | ||
throw new Error("\tReason: Given expected type \"" + expectedType + "\" not registered with JsonConvert.registerClasses()."); | ||
} | ||
expectedType = realExpectedType; | ||
} | ||
if (expectedType.prototype.hasOwnProperty(json_convert_options_1.Settings.CLASS_IDENTIFIER)) { | ||
@@ -1089,147 +1144,2 @@ return serialize ? | ||
throw new Error("\tReason: Mapping failed because of an unknown error."); | ||
/* | ||
// Map immediately if we don't care about the type | ||
if (expectedJsonType === Any || expectedJsonType === null || expectedJsonType === Object) { | ||
return value; | ||
} | ||
// Map the property to null if necessary | ||
if (value === undefined && this.mapUndefinedToNull) { | ||
value = null; | ||
} | ||
// Check if attempt and expected was 1-d | ||
if (expectedJsonType instanceof Array === false && value instanceof Array === false) { | ||
// Check the type | ||
if (typeof (expectedJsonType) !== "undefined" && expectedJsonType.prototype.hasOwnProperty(Settings.CLASS_IDENTIFIER)) { // only decorated custom objects have this injected property | ||
// Check if we have null value | ||
if (value === null) { | ||
if (this.valueCheckingMode !== ValueCheckingMode.DISALLOW_NULL) | ||
return null; | ||
else throw new Error("\tReason: Given value is null."); | ||
} | ||
if (serialize) return this.serializeObject(value, expectedJsonType); | ||
else return this.deserializeObject(value, expectedJsonType); | ||
} else if (expectedJsonType === Any || expectedJsonType === null || expectedJsonType === Object) { // general object | ||
// Check if we have null value | ||
if (value === null) { | ||
if (this.valueCheckingMode !== ValueCheckingMode.DISALLOW_NULL) | ||
return null; | ||
else throw new Error("\tReason: Given value is null."); | ||
} | ||
return value; | ||
} else if (expectedJsonType === String || expectedJsonType === Number || expectedJsonType === Boolean) { // otherwise check for a primitive type | ||
// Check if we have null value | ||
if (value === null) { | ||
if (this.valueCheckingMode === ValueCheckingMode.ALLOW_NULL) return null; | ||
else throw new Error("\tReason: Given value is null."); | ||
} | ||
// Check if the types match | ||
if ( // primitive types match | ||
(expectedJsonType === String && typeof (value) === "string") || | ||
(expectedJsonType === Number && typeof (value) === "number") || | ||
(expectedJsonType === Boolean && typeof (value) === "boolean") | ||
) { | ||
return value; | ||
} else { // primitive types mismatch | ||
if (this.ignorePrimitiveChecks) return value; | ||
throw new Error("\tReason: Given object does not match the expected primitive type."); | ||
} | ||
} else { // other weird types | ||
throw new Error( | ||
"\tReason: Expected type is unknown. There might be multiple reasons for this:\n" + | ||
"\t- You are missing the decorator @JsonObject (for object mapping)\n" + | ||
"\t- You are missing the decorator @JsonConverter (for custom mapping) before your class definition\n" + | ||
"\t- Your given class is undefined in the decorator because of circular dependencies" | ||
); | ||
} | ||
} | ||
// Check if expected was n-d | ||
if (expectedJsonType instanceof Array) { | ||
if (value === null) { | ||
if (this.valueCheckingMode !== ValueCheckingMode.DISALLOW_NULL) return null; | ||
else throw new Error("\tReason: Given value is null."); | ||
} | ||
// Check that value is not primitive | ||
if (value instanceof Object) { | ||
let array: any[] = []; | ||
// No data given, so return empty value | ||
if (value.length === 0) { | ||
return array; | ||
} | ||
// We obviously don't care about the type, so return the value as is | ||
if (expectedJsonType.length === 0) { | ||
return value; | ||
} | ||
// Copy the expectedJsonType array so we don't change the class-level mapping based on the value of this property | ||
const jsonType: any[] = expectedJsonType.slice(0); | ||
// Check if attempt was n-d | ||
if (value instanceof Array) { | ||
// Loop through the data. Both type and value are at least of length 1 | ||
let autofillType: boolean = jsonType.length < value.length; | ||
for (let i = 0; i < value.length; i++) { | ||
if (autofillType && i >= jsonType.length) { | ||
jsonType[i] = jsonType[i - 1]; | ||
} | ||
array[i] = this.verifyProperty(jsonType[i], value[i], serialize); | ||
} | ||
return array; | ||
// Otherwise attempt was 1-d | ||
} else { | ||
// Loop through the data. Both type and value are at least of length 1 | ||
let autofillType: boolean = jsonType.length < Object.keys(value).length; | ||
let i = 0; | ||
for (let key in value) { | ||
if (autofillType && i >= jsonType.length) { | ||
jsonType[i] = jsonType[i - 1]; | ||
} | ||
array[key as any] = this.verifyProperty(jsonType[i], value[key]); | ||
i++; | ||
} | ||
return array; | ||
} | ||
} else { | ||
throw new Error("\tReason: Expected type is array, but given value is primitive."); | ||
} | ||
} | ||
// Check if attempt was n-d and expected as 1-d | ||
if (value instanceof Array) { | ||
throw new Error("\tReason: Given value is array, but expected a non-array type."); | ||
} | ||
// All other attempts are fatal | ||
throw new Error("\tReason: Mapping failed because of an unknown error."); | ||
*/ | ||
}; | ||
@@ -1296,2 +1206,5 @@ /** | ||
} | ||
else if (typeof expectedJsonType === "string") { | ||
return expectedJsonType; | ||
} | ||
else if (expectedJsonType === undefined) { | ||
@@ -1298,0 +1211,0 @@ return "undefined"; |
@@ -10,3 +10,3 @@ /** | ||
* | ||
* @param target the class identifier or the class | ||
* @param classIdentifier the class identifier | ||
* | ||
@@ -17,3 +17,3 @@ * @returns | ||
*/ | ||
export declare function JsonObject(target: string | any): (target: any) => void; | ||
export declare function JsonObject(classIdentifier: string): (target: any) => void; | ||
/** | ||
@@ -20,0 +20,0 @@ * Decorator of a class property that comes from a JSON object. |
@@ -5,2 +5,6 @@ import { MappingOptions, Settings } from "./json-convert-options"; | ||
/** | ||
* Map of all registered json objects | ||
*/ | ||
var jsonObjectsMap = new Map(); | ||
/** | ||
* Decorator of a class that is a custom converter. | ||
@@ -16,3 +20,3 @@ * | ||
* | ||
* @param target the class identifier or the class | ||
* @param classIdentifier the class identifier | ||
* | ||
@@ -23,6 +27,16 @@ * @returns | ||
*/ | ||
export function JsonObject(target) { | ||
// target is the constructor or the custom class name | ||
var classIdentifier = ""; | ||
var decorator = function (target) { | ||
export function JsonObject(classIdentifier) { | ||
return function (target) { | ||
var _a; | ||
// Store the classIdentifier with the actual class reference | ||
if (jsonObjectsMap.has(classIdentifier)) { | ||
throw new Error("Fatal error in JsonConvert. " + | ||
"You must use unique class identifiers in the @JsonObject() decorator.\n\n" + | ||
"\tClass identifier: \n" + | ||
"\t\t" + classIdentifier + "\n\n" + | ||
"This class identifier has been already used for class \"" + ((_a = jsonObjectsMap.get(classIdentifier)) === null || _a === void 0 ? void 0 : _a.name) + "\".\n\n"); | ||
} | ||
else { | ||
jsonObjectsMap.set(classIdentifier, target); | ||
} | ||
target.prototype[Settings.CLASS_IDENTIFIER] = classIdentifier.length > 0 ? classIdentifier : target.name; | ||
@@ -44,18 +58,2 @@ var mapping = target.prototype[Settings.MAPPING_PROPERTY]; | ||
}; | ||
var type = typeof target; | ||
switch (type) { | ||
// Decorator was @JsonObject(classId) | ||
case "string": | ||
classIdentifier = target; | ||
return decorator; | ||
// Decorator was @JsonObject | ||
// Decorator was @JsonObject() | ||
// Decorator was @JsonObject(123) | ||
case "function": | ||
case "undefined": | ||
default: | ||
throw new Error("Fatal error in JsonConvert. " + | ||
"It is mandatory to pass a string as parameter in the @JsonObject decorator.\n\n" + | ||
"Use @JsonObject(classId) where classId is a string.\n\n"); | ||
} | ||
} | ||
@@ -62,0 +60,0 @@ /** |
@@ -260,4 +260,4 @@ import { PropertyConvertingMode } from "./json-convert-enums"; | ||
/** | ||
* Determines all classes which should use the discriminator feature. | ||
* Only classes provided here can be enriched with the discriminator property. | ||
* Determines all classes which should use the lazy-loading or discriminator feature. | ||
* Only classes provided here can be used with lazy-loading or the discriminator property. | ||
* | ||
@@ -268,4 +268,4 @@ * @see https://www.npmjs.com/package/json2typescript full documentation | ||
/* | ||
* Determines all classes which should use the discriminator feature. | ||
* Only classes provided here can be enriched with the discriminator property. | ||
* Determines all classes which should use the lazy-loading or discriminator feature. | ||
* Only classes provided here can be used with lazy-loading or the discriminator property. | ||
* | ||
@@ -388,5 +388,5 @@ * @see https://www.npmjs.com/package/json2typescript full documentation | ||
*/ | ||
deserialize<T extends object>(json: object | object[], classReference: { | ||
deserialize<T extends object>(json: object | object[], classReference?: { | ||
new (): T; | ||
}): T | T[]; | ||
} | null): T | T[]; | ||
/** | ||
@@ -404,5 +404,5 @@ * Tries to deserialize a JSON object to a TypeScript object. | ||
*/ | ||
deserializeObject<T extends object>(jsonObject: any, classReference: { | ||
deserializeObject<T extends object>(jsonObject: any, classReference?: { | ||
new (): T; | ||
}): T; | ||
} | null): T; | ||
/** | ||
@@ -420,6 +420,15 @@ * Tries to deserialize a JSON array to a TypeScript array. | ||
*/ | ||
deserializeArray<T extends object>(jsonArray: any[], classReference: { | ||
deserializeArray<T extends object>(jsonArray: any[], classReference?: { | ||
new (): T; | ||
}): T[]; | ||
} | null): T[]; | ||
/** | ||
* Returns the correct class reference for the provided JSON object. | ||
* If the provided class reference is null, the class reference is retrieved from the class map using the discriminator property. | ||
* | ||
* @param jsonObject the JSON object | ||
* @param classReference the class reference | ||
* @throws throws an Error in case of failure | ||
*/ | ||
private getRealClassReference; | ||
/** | ||
* Tries to find the JSON mapping for a given class property from the given instance used for mapping, | ||
@@ -426,0 +435,0 @@ * and finally assign the value from the given dataObject |
@@ -108,4 +108,4 @@ import { Any } from "./any"; | ||
/** | ||
* Determines all classes which should use the discriminator feature. | ||
* Only classes provided here can be enriched with the discriminator property. | ||
* Determines all classes which should use the lazy-loading or discriminator feature. | ||
* Only classes provided here can be used with lazy-loading or the discriminator property. | ||
* | ||
@@ -381,4 +381,4 @@ * @see https://www.npmjs.com/package/json2typescript full documentation | ||
/** | ||
* Determines all classes which should use the discriminator feature. | ||
* Only classes provided here can be enriched with the discriminator property. | ||
* Determines all classes which should use the lazy-loading or discriminator feature. | ||
* Only classes provided here can be used with lazy-loading or the discriminator property. | ||
* | ||
@@ -636,2 +636,3 @@ * @see https://www.npmjs.com/package/json2typescript full documentation | ||
JsonConvert.prototype.deserialize = function (json, classReference) { | ||
if (classReference === void 0) { classReference = null; } | ||
if (this.operationMode === OperationMode.DISABLE) { | ||
@@ -666,5 +667,7 @@ return json; | ||
JsonConvert.prototype.deserializeObject = function (jsonObject, classReference) { | ||
if (classReference === void 0) { classReference = null; } | ||
if (this.operationMode === OperationMode.DISABLE) { | ||
return jsonObject; | ||
} | ||
var realClassReference = this.getRealClassReference(jsonObject, classReference); | ||
jsonObject = this.mapUndefinedToNull && jsonObject === undefined ? null : jsonObject; | ||
@@ -699,3 +702,3 @@ // Check if the passed type is allowed | ||
} | ||
var instance = new classReference(); | ||
var instance = new realClassReference(); | ||
// Loop through all initialized class properties | ||
@@ -736,2 +739,3 @@ for (var _i = 0, _a = Object.keys(instance); _i < _a.length; _i++) { | ||
JsonConvert.prototype.deserializeArray = function (jsonArray, classReference) { | ||
if (classReference === void 0) { classReference = null; } | ||
if (this.operationMode === OperationMode.DISABLE) { | ||
@@ -786,2 +790,45 @@ return jsonArray; | ||
/** | ||
* Returns the correct class reference for the provided JSON object. | ||
* If the provided class reference is null, the class reference is retrieved from the class map using the discriminator property. | ||
* | ||
* @param jsonObject the JSON object | ||
* @param classReference the class reference | ||
* @throws throws an Error in case of failure | ||
*/ | ||
JsonConvert.prototype.getRealClassReference = function (jsonObject, classReference) { | ||
var _a; | ||
// First determine if the discriminator is used or not | ||
if (this.useDiscriminator) { | ||
// Check if we find the $type property. If not, throw an error. | ||
if (jsonObject.hasOwnProperty(this.discriminatorPropertyName)) { | ||
var discriminatorValue = (_a = jsonObject[this.discriminatorPropertyName]) !== null && _a !== void 0 ? _a : ""; | ||
var classReferenceNameFromMap = this.classes.get(discriminatorValue); | ||
if (classReferenceNameFromMap !== undefined && classReferenceNameFromMap !== null) { | ||
return classReferenceNameFromMap; | ||
} | ||
else { | ||
throw new Error("Fatal error in JsonConvert. " + | ||
"Discriminator value \"" + discriminatorValue + "\" could not be found in the registered classes. " + | ||
"Make sure you register the class using the method JsonConvert.registerClasses(" + discriminatorValue + ")" + | ||
"\n"); | ||
} | ||
} | ||
else { | ||
throw new Error("Fatal error in JsonConvert. " + | ||
"Discriminator property \"" + this.discriminatorPropertyName + "\" is missing in JSON object." + | ||
"\n"); | ||
} | ||
} | ||
else { | ||
// Make sure the class reference is given for if the discriminator is disabled | ||
if (classReference === null) { | ||
throw new Error("Fatal error in JsonConvert. " + | ||
"Passed parameter classReference in JsonConvert.deserialize() is null. " + | ||
"This is only allowed if discriminator feature is enabled." + | ||
"\n"); | ||
} | ||
return classReference; | ||
} | ||
}; | ||
/** | ||
* Tries to find the JSON mapping for a given class property from the given instance used for mapping, | ||
@@ -1035,3 +1082,11 @@ * and finally assign the value from the given dataObject | ||
// Check if objects match | ||
if (expectedType instanceof Object && value instanceof Object) { | ||
if ((expectedType instanceof Object || typeof expectedType === "string") && value instanceof Object) { | ||
// If the expected type is a string (means: lazy-loading), get the real type from the registered classes | ||
if (typeof expectedType === "string") { | ||
var realExpectedType = this.classes.get(expectedType); | ||
if (!realExpectedType) { | ||
throw new Error("\tReason: Given expected type \"" + expectedType + "\" not registered with JsonConvert.registerClasses()."); | ||
} | ||
expectedType = realExpectedType; | ||
} | ||
if (expectedType.prototype.hasOwnProperty(Settings.CLASS_IDENTIFIER)) { | ||
@@ -1086,147 +1141,2 @@ return serialize ? | ||
throw new Error("\tReason: Mapping failed because of an unknown error."); | ||
/* | ||
// Map immediately if we don't care about the type | ||
if (expectedJsonType === Any || expectedJsonType === null || expectedJsonType === Object) { | ||
return value; | ||
} | ||
// Map the property to null if necessary | ||
if (value === undefined && this.mapUndefinedToNull) { | ||
value = null; | ||
} | ||
// Check if attempt and expected was 1-d | ||
if (expectedJsonType instanceof Array === false && value instanceof Array === false) { | ||
// Check the type | ||
if (typeof (expectedJsonType) !== "undefined" && expectedJsonType.prototype.hasOwnProperty(Settings.CLASS_IDENTIFIER)) { // only decorated custom objects have this injected property | ||
// Check if we have null value | ||
if (value === null) { | ||
if (this.valueCheckingMode !== ValueCheckingMode.DISALLOW_NULL) | ||
return null; | ||
else throw new Error("\tReason: Given value is null."); | ||
} | ||
if (serialize) return this.serializeObject(value, expectedJsonType); | ||
else return this.deserializeObject(value, expectedJsonType); | ||
} else if (expectedJsonType === Any || expectedJsonType === null || expectedJsonType === Object) { // general object | ||
// Check if we have null value | ||
if (value === null) { | ||
if (this.valueCheckingMode !== ValueCheckingMode.DISALLOW_NULL) | ||
return null; | ||
else throw new Error("\tReason: Given value is null."); | ||
} | ||
return value; | ||
} else if (expectedJsonType === String || expectedJsonType === Number || expectedJsonType === Boolean) { // otherwise check for a primitive type | ||
// Check if we have null value | ||
if (value === null) { | ||
if (this.valueCheckingMode === ValueCheckingMode.ALLOW_NULL) return null; | ||
else throw new Error("\tReason: Given value is null."); | ||
} | ||
// Check if the types match | ||
if ( // primitive types match | ||
(expectedJsonType === String && typeof (value) === "string") || | ||
(expectedJsonType === Number && typeof (value) === "number") || | ||
(expectedJsonType === Boolean && typeof (value) === "boolean") | ||
) { | ||
return value; | ||
} else { // primitive types mismatch | ||
if (this.ignorePrimitiveChecks) return value; | ||
throw new Error("\tReason: Given object does not match the expected primitive type."); | ||
} | ||
} else { // other weird types | ||
throw new Error( | ||
"\tReason: Expected type is unknown. There might be multiple reasons for this:\n" + | ||
"\t- You are missing the decorator @JsonObject (for object mapping)\n" + | ||
"\t- You are missing the decorator @JsonConverter (for custom mapping) before your class definition\n" + | ||
"\t- Your given class is undefined in the decorator because of circular dependencies" | ||
); | ||
} | ||
} | ||
// Check if expected was n-d | ||
if (expectedJsonType instanceof Array) { | ||
if (value === null) { | ||
if (this.valueCheckingMode !== ValueCheckingMode.DISALLOW_NULL) return null; | ||
else throw new Error("\tReason: Given value is null."); | ||
} | ||
// Check that value is not primitive | ||
if (value instanceof Object) { | ||
let array: any[] = []; | ||
// No data given, so return empty value | ||
if (value.length === 0) { | ||
return array; | ||
} | ||
// We obviously don't care about the type, so return the value as is | ||
if (expectedJsonType.length === 0) { | ||
return value; | ||
} | ||
// Copy the expectedJsonType array so we don't change the class-level mapping based on the value of this property | ||
const jsonType: any[] = expectedJsonType.slice(0); | ||
// Check if attempt was n-d | ||
if (value instanceof Array) { | ||
// Loop through the data. Both type and value are at least of length 1 | ||
let autofillType: boolean = jsonType.length < value.length; | ||
for (let i = 0; i < value.length; i++) { | ||
if (autofillType && i >= jsonType.length) { | ||
jsonType[i] = jsonType[i - 1]; | ||
} | ||
array[i] = this.verifyProperty(jsonType[i], value[i], serialize); | ||
} | ||
return array; | ||
// Otherwise attempt was 1-d | ||
} else { | ||
// Loop through the data. Both type and value are at least of length 1 | ||
let autofillType: boolean = jsonType.length < Object.keys(value).length; | ||
let i = 0; | ||
for (let key in value) { | ||
if (autofillType && i >= jsonType.length) { | ||
jsonType[i] = jsonType[i - 1]; | ||
} | ||
array[key as any] = this.verifyProperty(jsonType[i], value[key]); | ||
i++; | ||
} | ||
return array; | ||
} | ||
} else { | ||
throw new Error("\tReason: Expected type is array, but given value is primitive."); | ||
} | ||
} | ||
// Check if attempt was n-d and expected as 1-d | ||
if (value instanceof Array) { | ||
throw new Error("\tReason: Given value is array, but expected a non-array type."); | ||
} | ||
// All other attempts are fatal | ||
throw new Error("\tReason: Mapping failed because of an unknown error."); | ||
*/ | ||
}; | ||
@@ -1293,2 +1203,5 @@ /** | ||
} | ||
else if (typeof expectedJsonType === "string") { | ||
return expectedJsonType; | ||
} | ||
else if (expectedJsonType === undefined) { | ||
@@ -1295,0 +1208,0 @@ return "undefined"; |
{ | ||
"name": "json2typescript", | ||
"version": "1.5.0-rc.0", | ||
"version": "1.5.0", | ||
"description": "Provides TypeScript methods to map a JSON object to a JavaScript object on runtime", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -41,4 +41,4 @@ [![npm](https://img.shields.io/npm/v/json2typescript.svg)](https://www.npmjs.com/package/json2typescript) | ||
> Warning: We earlier suggested to use the `@JsonObject(classId)` decorator, but did not enforce it. | ||
> Since v1.4.0, this is mandatory in order to make (de)serialization work properly with class inheritance. | ||
> Warning: We earlier suggested to use the `@JsonObject(classIdentifier)` decorator, but did not enforce it. | ||
> Since v1.4.0, it is mandatory to use a unique classIdentifier for every class in order to make (de)serialization work properly with class inheritance. | ||
> In versions above v1.2.0 and below v1.4.0, it is possible to run into issues when not using the decorator. | ||
@@ -91,7 +91,7 @@ | ||
* Classes need to be preceeded by `@JsonObject(classIdentifier)` | ||
* Properties need to be preceeded by `@JsonProperty(jsonProperty, conversionOption, convertingMode)` | ||
* Classes need to be preceded by `@JsonObject(classIdentifier)` where the classIdentifier is unique in the whole project | ||
* Properties need to be preceded by `@JsonProperty(jsonProperty, conversionOption, convertingMode)` | ||
* Properties need to have a default value (or undefined), otherwise the mapper will not work | ||
See below an example so you can learn from it how **json2typescript** works best. | ||
See below an example, so you can learn from it how **json2typescript** works best. | ||
@@ -104,3 +104,3 @@ Assuming that you have created the **testApplication** in the step before and installed **json2typescript** as | ||
@JsonObject("City") | ||
@JsonObject("City") // Make sure "City" is a unique identifier for this class | ||
export class City { | ||
@@ -159,3 +159,3 @@ | ||
@JsonObject("Country") | ||
@JsonObject("Country") // Make sure "Country" is a unique identifier for this class | ||
export class Country { | ||
@@ -339,2 +339,6 @@ | ||
> Tip: It is possible to lazy-load custom classes to prevent circular dependencies. | ||
> Then, the expected type for custom classes can be written as a string. | ||
> Please read further below how to implement this feature. | ||
##### Adding a custom converter | ||
@@ -627,3 +631,3 @@ | ||
`(T | T[]) deserialize<T extends object>(json: any, classReference: { new(): T })` | ||
`(T | T[]) deserialize<T extends object>(json: any, classReference: { new(): T } | null = null)` | ||
@@ -633,2 +637,3 @@ Tries to deserialize given JSON to a TypeScript object or array of objects. | ||
The first parameter must be a Typescript object or array, the second parameter is the class reference. | ||
If the discriminator feature is used, the class reference is optional. | ||
@@ -643,13 +648,13 @@ The returned value will be an instance or an array of instances of the given class reference. | ||
Registers a list of classes to be used in the discriminator feature. | ||
Registers a list of classes to be used with lazy-loading and in the discriminator feature. | ||
`void ungisterClasses(...classReferences: { new(): any }[])` | ||
Unregisters a list of classes from the discriminator feature. | ||
Unregisters a list of classes from lazy-loading and the discriminator feature. | ||
`void unregisterAllClasses()` | ||
Unregisters all classes from the discriminator feature. | ||
Unregisters all classes from lazy-loading and the discriminator feature. | ||
> Note: You only need to register and unregister classes if you use the discriminator feature. Otherwise, these methods are without any effect. | ||
> Note: You only need to register and unregister classes if you use lazy-loading or the discriminator feature. Otherwise, these methods are without any effect. | ||
@@ -739,2 +744,44 @@ #### Other methods | ||
## Instantiation with the lazy-loading feature to avoid circular dependencies | ||
In some scenarios, developers run into circular dependencies when declaring objects. | ||
A simple example would be two classes importing each other: | ||
```typescript | ||
import { User } from "./user"; | ||
@JsonObject("Team") | ||
export class Team { | ||
@JsonProperty("users", [User]) | ||
user: User[] = []; | ||
} | ||
``` | ||
```typescript | ||
import { Team } from "./team"; | ||
@JsonObject("User") | ||
export class User { | ||
@JsonProperty("teams", [Team]) | ||
team: Team[] = []; | ||
} | ||
``` | ||
This is generally not possible because of the circular dependency. | ||
But since TypeScript 3.8, you may use [type imports](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html) in order to bypass circular dependencies. | ||
`json2typescript` implements a lazy-loading feature so that the above example is possible. | ||
The following steps need to be done: | ||
- Import only the type, for example by writing `import type { User } from "./user";`. Note that we added the `type` keyword. | ||
- Do not use the class reference in the `@JsonProperty` decorator, but instead use the classIdentifier (as string) given in the `@JsonObject` decorator. | ||
For example, write `@JsonProperty("users", ["User"])`. Note that we added the `"` around the User. | ||
- Register all lazy-loading classes before ever using them with `json2typescript`. | ||
You may simply call `jsonConvert.registerClasses(User, Team)` for the example above. | ||
Now you can use the classes `User` and `Team` as expected. | ||
> Note: Using type imports may cause other problems in your classes because the imports may only be used for type contexts. | ||
> For example, you will not be able to write `new Team()` in the `User` class at all. | ||
> However, you still will be able to use `Team` as a type in the `User` class and access all its properties and methods. | ||
## Automatic instantiation using the discriminator feature | ||
@@ -741,0 +788,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
0
852
0
299344
4377