@mdcc/at-json
Version:
A declarative mapper to and from JSON.
884 lines (823 loc) • 34.6 kB
JavaScript
/******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ([
/* 0 */,
/* 1 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ JsonArray: () => (/* reexport safe */ _decorators__WEBPACK_IMPORTED_MODULE_0__.JsonArray),
/* harmony export */ JsonArrayOfComplexProperty: () => (/* reexport safe */ _decorators__WEBPACK_IMPORTED_MODULE_0__.JsonArrayOfComplexProperty),
/* harmony export */ JsonClass: () => (/* reexport safe */ _decorators__WEBPACK_IMPORTED_MODULE_0__.JsonClass),
/* harmony export */ JsonComplexProperty: () => (/* reexport safe */ _decorators__WEBPACK_IMPORTED_MODULE_0__.JsonComplexProperty),
/* harmony export */ JsonMap: () => (/* reexport safe */ _decorators__WEBPACK_IMPORTED_MODULE_0__.JsonMap),
/* harmony export */ JsonMapper: () => (/* reexport safe */ _mapper__WEBPACK_IMPORTED_MODULE_1__.JsonMapper),
/* harmony export */ JsonProperty: () => (/* reexport safe */ _decorators__WEBPACK_IMPORTED_MODULE_0__.JsonProperty),
/* harmony export */ makeCustomDecorator: () => (/* reexport safe */ _decorators__WEBPACK_IMPORTED_MODULE_0__.makeCustomDecorator)
/* harmony export */ });
/* harmony import */ var _decorators__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
/* harmony import */ var _mapper__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12);
/***/ }),
/* 2 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ JsonArray: () => (/* reexport safe */ _array__WEBPACK_IMPORTED_MODULE_0__.JsonArray),
/* harmony export */ JsonArrayOfComplexProperty: () => (/* reexport safe */ _array_of_complex_property__WEBPACK_IMPORTED_MODULE_1__.JsonArrayOfComplexProperty),
/* harmony export */ JsonClass: () => (/* reexport safe */ _class__WEBPACK_IMPORTED_MODULE_2__.JsonClass),
/* harmony export */ JsonComplexProperty: () => (/* reexport safe */ _complex_property__WEBPACK_IMPORTED_MODULE_4__.JsonComplexProperty),
/* harmony export */ JsonMap: () => (/* reexport safe */ _map__WEBPACK_IMPORTED_MODULE_5__.JsonMap),
/* harmony export */ JsonProperty: () => (/* reexport safe */ _property__WEBPACK_IMPORTED_MODULE_6__.JsonProperty),
/* harmony export */ makeCustomDecorator: () => (/* reexport safe */ _common__WEBPACK_IMPORTED_MODULE_3__.makeCustomDecorator)
/* harmony export */ });
/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3);
/* harmony import */ var _array_of_complex_property__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7);
/* harmony import */ var _class__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(8);
/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4);
/* harmony import */ var _complex_property__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(9);
/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(10);
/* harmony import */ var _property__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(11);
/***/ }),
/* 3 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ JsonArray: () => (/* binding */ JsonArray)
/* harmony export */ });
/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4);
/**
* The basic decorator for array of simple properties.
*
* `params` has the same meaning that the one in {@link JsonProperty}.
*
* Usage examples:
* ```typescript
* import { JsonClass, JsonMapper, JsonArray } from '@mdcc/at-json';
*
* @JsonClass()
* class MyClass
* {
* @JsonArray()
* basicProperty: string[];
*
* @JsonArray('extName')
* renamedProperty: number[];
*
* @JsonArray({
* name: 'custom',
* serialize: (mapper, n) => n.toString(),
* deserialize: (mapper, n) => parseInt(n, 10)
* }, true)
* customProperty: number[];
* }
*
* const backendObject = {
* basicProperty: ['value', 'value2'],
* extName: [123, 456],
* customProperty: ['456', '789']
* };
* const mapper = new JsonMapper();
* const deserialized = mapper.deserialize<MyClass>(MyClass, backendObject);
*
* // basicProperty keeps the same name
* assert.equal(deserialized.basicProperty, ['value', 'value2']);
* // extName became renamedProperty
* assert.equal(deserialized.renamedProperty, [123, 456]);
* // customProperty became custom, and the string was converted to number
* assert.equal(deserialized.custom, [456, 789]);
*
* const backendObjectSerialized = mapper.serialize(deserialized);
* // reverse conversion was performed
* assert.deepEqual(backendObjectSerialized, backendObject);
*
* const errorObject = { basicProperty: {} };
* const deserializedErrorObject = mapper.deserialize<MyClass>(MyClass, errorObject);
* // basicProperty is null
* assert.isNull(deserializedErrorObject.basicProperty);
*
* const errorObject2 = { basicProperty: [], renamedProperty: [], customProperty: {} };
* // this throws because customProperty was decorated with `@JsonArray(..., true)`
* const deserializedErrorObject2 = mapper.deserialize<MyClass>(MyClass, errorObject2);
* ```
*
* @export
* @param {DecoratorInputWithCustomFunctions<T>} [params] the params
* @param {NoCustomFunctionsDecoratorInput} params params
* @param {boolean} throwIfNotArray if true, throws an error if the property is not an array.
* @returns the decorator for the property.
*/
function JsonArray(params, throwIfNotArray) {
return (0,_common__WEBPACK_IMPORTED_MODULE_0__.transformDecorator)(opt => ({
serialize: (mapper, array) => (0,_common__WEBPACK_IMPORTED_MODULE_0__.mapArray)(mapper, array, opt.serialize, throwIfNotArray),
deserialize: (mapper, array) => (0,_common__WEBPACK_IMPORTED_MODULE_0__.mapArray)(mapper, array, opt.deserialize, throwIfNotArray),
}))(params);
}
/***/ }),
/* 4 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ makeCustomDecorator: () => (/* binding */ makeCustomDecorator),
/* harmony export */ mapArray: () => (/* binding */ mapArray),
/* harmony export */ transformDecorator: () => (/* binding */ transformDecorator)
/* harmony export */ });
/* harmony import */ var _interfaces__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(5);
/* harmony import */ var _reflection__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6);
function normalizeParams(params) {
let resolvedParams;
if (typeof params === 'string') {
resolvedParams = { name: params };
}
else {
resolvedParams = params || {};
}
return resolvedParams;
}
/**
* A custom decorator factory function, in order to allow defining custom reusable decorators.
*
* @param serializeFn the function used for serializing the value.
* @param deserializeFn the function used for deserializing the value.
*/
function makeCustomDecorator(fn) {
return params => {
const actualParams = {
...normalizeParams(params),
...fn(),
};
return _createPropertyDecorator(actualParams);
};
}
/**
* A decorator transforming function, used internally.
*/
function transformDecorator(fn) {
return params => {
const normalizedParams = normalizeParams(params);
const { serialize, deserialize } = fn(normalizedParams);
const actualParams = {
...normalizedParams,
serialize,
deserialize,
};
return _createPropertyDecorator(actualParams);
};
}
function _createPropertyDecorator(actualParams) {
return function (target, propertyKey) {
const constructor = target.constructor;
const objMetadata = (0,_reflection__WEBPACK_IMPORTED_MODULE_1__.getMetadata)(_interfaces__WEBPACK_IMPORTED_MODULE_0__.Symbols.fieldsMetadata, constructor) || [];
(0,_reflection__WEBPACK_IMPORTED_MODULE_1__.defineMetadata)(_interfaces__WEBPACK_IMPORTED_MODULE_0__.Symbols.fieldsMetadata, [...objMetadata, propertyKey], constructor);
(0,_reflection__WEBPACK_IMPORTED_MODULE_1__.defineMetadata)(_interfaces__WEBPACK_IMPORTED_MODULE_0__.Symbols.mappingMetadata, actualParams, constructor, propertyKey);
};
}
function mapArray(mapper, propertyValue, deserializeItem, throwIfNotArray) {
if (Array.isArray(propertyValue)) {
if (deserializeItem) {
// map deserialize on the array
return propertyValue.map(item => deserializeItem(mapper, item));
}
else {
return propertyValue;
}
}
else {
if (throwIfNotArray) {
throw new Error(`Expected array, got ${typeof propertyValue}`);
}
// if marked as array, but not an array, set the value to null
return null;
}
}
/***/ }),
/* 5 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Symbols: () => (/* binding */ Symbols),
/* harmony export */ hasAfterDeserialize: () => (/* binding */ hasAfterDeserialize),
/* harmony export */ hasCustomSerializeExport: () => (/* binding */ hasCustomSerializeExport)
/* harmony export */ });
const Symbols = {
mappingMetadata: Symbol('[[mapping]]'),
mappingOptions: Symbol('[[mappingOptions]]'),
fieldsMetadata: Symbol('[[fields]]'),
metadataRoot: Symbol('[[AtJsonMetadata]]'),
};
Object.freeze(Symbols);
/**
* Type guard for {@link CustomSerialize} interface.
*
* @param mapValue value to check
* @returns if the parameter is a CustomSerialize interface
*/
function hasCustomSerializeExport(mapValue) {
const fn = mapValue[nameOf('customSerialize')];
return typeof fn === 'function';
}
/**
* Type guard for {@link AfterDeserialize} interface.
*
* @param mapValue value to check
* @returns if the parameter is a AfterDeserialize interface
*/
function hasAfterDeserialize(mapValue) {
const fn = mapValue[nameOf('afterDeserialize')];
return typeof fn === 'function';
}
/** helper */
function nameOf(k) {
return k;
}
/***/ }),
/* 6 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ defineMetadata: () => (/* binding */ defineMetadata),
/* harmony export */ getMetadata: () => (/* binding */ getMetadata)
/* harmony export */ });
/* harmony import */ var _interfaces__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(5);
/**
* Defines a metadata on the target object.
*
* Replaces `Reflect.defineMetadata`.
*
* @param {string | symbol} key the key of the metadata
* @param {any} value the value to be set in the metadata
* @param {object} target the target on which the metadata should be set
* @param {string | symbol} propertyName optional property name to add to the path on where value is set
*/
function defineMetadata(key, value, target, propertyName) {
// define metadata map on object to avoid overwriting the parent
if (!has(target, _interfaces__WEBPACK_IMPORTED_MODULE_0__.Symbols.metadataRoot)) {
target[_interfaces__WEBPACK_IMPORTED_MODULE_0__.Symbols.metadataRoot] = {};
}
const v = target[_interfaces__WEBPACK_IMPORTED_MODULE_0__.Symbols.metadataRoot];
if (typeof v[propertyName] === 'undefined') {
v[propertyName] = {};
}
v[propertyName][key] = value;
}
/**
* Retrieves the metadata associated to `target` and `propertyName`.
*
* If not found, follows the prototype chain.
*
* Replaces `Reflect.getMedadata`.
*
* @param {string | symbol} key the key of the metadata
* @param {object} target the target from which the metadata should be retrieved
* @param {string | symbol} propertyName optional property name to add to the path on where value is retrieved
* @returns {any} the value found, or `undefined` if not found
*/
function getMetadata(key, target, propertyName) {
const propName = propertyName;
while (target) {
const map = target?.[_interfaces__WEBPACK_IMPORTED_MODULE_0__.Symbols.metadataRoot];
if (map && has(map, propName) && has(map[propName], key)) {
return map[propName][key];
}
target = Object.getPrototypeOf(target);
}
return undefined;
}
function has(object, key) {
return Object.prototype.hasOwnProperty.call(object, key);
}
/***/ }),
/* 7 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ JsonArrayOfComplexProperty: () => (/* binding */ JsonArrayOfComplexProperty)
/* harmony export */ });
/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4);
/**
* Decorator for complex-type array properties to be (de)serialized correctly.
* Use this if the property is an array of a type that needs recursive (de)serialization.
*
* Usage examples:
* ```typescript
* import {
* JsonClass,
* JsonMapper,
* JsonProperty,
* JsonArrayOfComplexProperty
* } from '@mdcc/at-json';
*
* @JsonClass()
* class SubClass
* {
* @JsonProperty()
* foo: string;
* }
*
* @JsonClass()
* class MyClass
* {
* @JsonArrayOfComplexProperty(SubClass)
* sub1: SubClass[];
*
* @JsonArrayOfComplexProperty(SubClass, 'extSub2', true)
* sub2: SubClass[];
* }
*
* const backendObject = {
* sub1: [{ foo: 'bar' }],
* extSub2: [{ foo: 'baz' }]
* };
* const mapper = new JsonMapper();
* const deserialized = mapper.deserialize<MyClass>(MyClass, backendObject);
*
* // sub1 keeps the same name
* assert.isInstanceOf(deserialized.sub1, Array);
* assert.isInstanceOf(deserialized.sub1[0], SubClass);
* assert.equal(deserialized.sub1[0].foo, 'bar');
*
* // extSub2 became sub2
* assert.isInstanceOf(deserialized.sub2, Array);
* assert.isInstanceOf(deserialized.sub2[0], SubClass);
* assert.equal(deserialized.sub2[0].foo, 'baz');
*
* const backendObjectSerialized = mapper.serialize(deserialized);
* // reverse conversion was performed
* assert.deepEqual(backendObjectSerialized, backendObject);
*
* const errorObject = { sub1: {} };
* const deserializedErrorObject = mapper.deserialize<MyClass>(MyClass, errorObject);
* // sub1 is null
* assert.isNull(deserializedErrorObject.sub1);
*
* const errorObject2 = { sub1: [], sub2: {} };
* // this throws because sub2 was decorated with `@JsonArrayOfComplexProperty(..., true)`
* const deserializedErrorObject2 = mapper.deserialize<MyClass>(MyClass, errorObject2);
* ```
*
* @export
* @param {Constructable<any>} constructor the constructor type of the array items.
* @param {DecoratorInputWithoutCustomFunctions} params params
* @param {boolean} throwIfNotArray if true, throws an error if the property is not an array.
* @returns the decorator for the property.
*/
function JsonArrayOfComplexProperty(constructor, params, throwIfNotArray) {
function serialize(m, item) {
return m.serialize(item);
}
function deserialize(m, item) {
return item === null || item === undefined ? item : m.deserialize(constructor, item);
}
return (0,_common__WEBPACK_IMPORTED_MODULE_0__.makeCustomDecorator)(() => ({
serialize: (mapper, array) => (0,_common__WEBPACK_IMPORTED_MODULE_0__.mapArray)(mapper, array, serialize, throwIfNotArray),
deserialize: (mapper, array) => (0,_common__WEBPACK_IMPORTED_MODULE_0__.mapArray)(mapper, array, deserialize, throwIfNotArray),
}))(params);
}
/***/ }),
/* 8 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ JsonClass: () => (/* binding */ JsonClass)
/* harmony export */ });
/* harmony import */ var _interfaces__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(5);
/* harmony import */ var _reflection__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6);
/**
* Decorator for mapped classes.
*
* @export
* @template T
* @returns
* @param ignoreMissingFields
*/
function JsonClass(options) {
const actualOptions = Object.assign({ ignoreUndecoratedProperties: true }, options);
return ctor => {
(0,_reflection__WEBPACK_IMPORTED_MODULE_1__.defineMetadata)(_interfaces__WEBPACK_IMPORTED_MODULE_0__.Symbols.mappingOptions, actualOptions, ctor);
return ctor;
};
}
/***/ }),
/* 9 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ JsonComplexProperty: () => (/* binding */ JsonComplexProperty)
/* harmony export */ });
/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4);
/**
* Decorator for complex-type properties to be (de)serialized correctly.
* Use this if the property is of a non-array type that needs recursive (de)serialization.
*
* Uses only the `name` property of the params.
*
* Usage examples:
* ```typescript
* import { JsonClass, JsonMapper, JsonProperty, JsonComplexProperty } from '@mdcc/at-json';
*
* @JsonClass()
* class SubClass
* {
* @JsonProperty()
* foo: string;
* }
*
* @JsonClass()
* class MyClass
* {
* @JsonComplexProperty(SubClass)
* sub1: SubClass;
*
* @JsonComplexProperty(SubClass, 'extSub2')
* sub2: SubClass;
* }
*
* const backendObject = {
* sub1: { foo: 'bar' },
* extSub2: { foo: 'baz' }
* };
* const mapper = new JsonMapper();
* const deserialized = mapper.deserialize<MyClass>(MyClass, backendObject);
*
* // sub1 keeps the same name
* assert.isInstanceOf(deserialized.sub1, SubClass);
* assert.equal(deserialized.sub1.foo, 'bar');
*
* // extSub2 became sub2
* assert.isInstanceOf(deserialized.sub2, SubClass);
* assert.equal(deserialized.sub2.foo, 'baz');
*
* const backendObjectSerialized = mapper.serialize(deserialized);
* // reverse conversion was performed
* assert.deepEqual(backendObjectSerialized, backendObject);
* ```
*
* @export
* @param {Constructable<any>} constructor the constructor type of the property.
* @param name the name of the property
* @returns the decorator for the property.
*/
function JsonComplexProperty(constructor, params) {
return (0,_common__WEBPACK_IMPORTED_MODULE_0__.makeCustomDecorator)(() => ({
serialize: (mapper, value) => mapper.serialize(value),
deserialize: (mapper, value) => mapper.deserialize(constructor, value),
}))(params);
}
/***/ }),
/* 10 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ JsonMap: () => (/* binding */ JsonMap)
/* harmony export */ });
/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4);
/**
* A custom decorator for handling objects as maps.
*
* @param params the mapping options to apply to the values of the map.
*/
function JsonMap(params) {
return (0,_common__WEBPACK_IMPORTED_MODULE_0__.makeCustomDecorator)(() => ({
serialize: (mapper, map) => {
const ret = {};
if (params?.complexType) {
for (const [k, v] of map.entries()) {
ret[k] = mapper.serialize(v);
}
}
else {
for (const [k, v] of map.entries()) {
ret[k] = v;
}
}
return ret;
},
deserialize: (mapper, obj) => {
const map = new Map();
if (params?.complexType) {
for (const key in obj) {
map.set(key, mapper.deserialize(params.complexType, obj[key]));
}
}
else {
for (const key in obj) {
map.set(key, obj[key]);
}
}
return map;
},
}))(params);
}
/***/ }),
/* 11 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ JsonProperty: () => (/* binding */ JsonProperty)
/* harmony export */ });
/* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4);
/**
* The basic decorator for simple properties.
*
* It basically takes the incoming property value, applies eventual custom serialization or deserialization,
* and then sets the property value to the result.
*
* `params` can be:
* - a string, if only the name of source property is provided (`name` property of {@link IMappingOptions}).
* - an object compliant to {@link IMappingOptions} interface.
*
* Usage examples:
* ```typescript
* import { JsonClass, JsonMapper, JsonProperty } from '@mdcc/at-json';
*
* @JsonClass()
* class MyClass
* {
* @JsonProperty()
* basicProperty: string;
*
* @JsonProperty('extName')
* renamedProperty: number;
*
* @JsonProperty({
* name: 'custom',
* serialize: (mapper, n) => n.toString(),
* deserialize: (mapper, n) => parseInt(n, 10)
* })
* customProperty: number;
* }
*
* const mapper = new JsonMapper();
* const backendObject = { basicProperty: 'value', extName: 123, customProperty: '456' };
* const deserialized = mapper.deserialize<MyClass>(MyClass, backendObject);
*
* // basicProperty keeps the same name
* assert.equal(deserialized.basicProperty, 'value');
* // extName became renamedProperty
* assert.equal(deserialized.renamedProperty, 123);
* // customProperty became custom, and the string was converted to number
* assert.equal(deserialized.custom, 456);
*
* const backendObjectSerialized = mapper.serialize(deserialized);
* // reverse conversion was performed
* assert.deepEqual(backendObjectSerialized, backendObject);
* ```
*
* @export
* @param {DecoratorInputWithCustomFunctions} [params] the params
* @returns the decorator for the property.
*/
function JsonProperty(params) {
return (0,_common__WEBPACK_IMPORTED_MODULE_0__.transformDecorator)(opt => ({
serialize: opt.serialize,
deserialize: opt.deserialize,
}))(params);
}
/***/ }),
/* 12 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ JsonMapper: () => (/* binding */ JsonMapper)
/* harmony export */ });
/* harmony import */ var _interfaces__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(5);
/* harmony import */ var _reflection__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6);
/**
* Class for JSON Mapping.
*
* @export
* @class JsonMapper
*/
class JsonMapper {
/**
* Serialization method.
* Transforms `source` into a new JSON value by applying the "serialization" step for each property decorator.
*
* Annotated properties are serialized into a property using the `name` value as the destination name (defaults to the property name).
*
* @param {*} source the value to be serialized.
* @returns {string} the transformed JSON value.
* @throws An error if a class encountered while serializing has no {@link JsonClass} decorator.
* @memberof JsonMapper
*/
serialize(source) {
// if val is nil no need to do anything
if (source === null || source === undefined) {
return source;
}
// retrieve the object constructor in order to load the class decorator metadata
const ctor = Object.getPrototypeOf(source).constructor;
const ctorOptions = (0,_reflection__WEBPACK_IMPORTED_MODULE_1__.getMetadata)(_interfaces__WEBPACK_IMPORTED_MODULE_0__.Symbols.mappingOptions, ctor);
if (!ctorOptions) {
throw new Error(`Class ${ctor.name} is not decorated with @JsonClass`);
}
// let's try to call an eventual custom export before the standard one
const customExport = exportCustom(this, source);
if (customExport.serialized) {
return customExport.value;
}
// should missing properties be ignored
const { ignoreUndecoratedProperties } = ctorOptions;
// destination object for the mapping
const target = {};
Object.keys(source).forEach(propName => {
// retrieve the serialization options in the decorator of the property
const options = (0,_reflection__WEBPACK_IMPORTED_MODULE_1__.getMetadata)(_interfaces__WEBPACK_IMPORTED_MODULE_0__.Symbols.mappingMetadata, ctor, propName);
const propValue = source[propName];
// if no decorator is provided, map the property by copy if "ignoreUndecoratedProperties" is false
// maybe here a clone should be used instead of a shallow copy
if (options === undefined) {
if (!ignoreUndecoratedProperties) {
target[propName] = propValue;
}
return;
}
// destination property name, by default the property name in the source object, but it can be
// overridden in the decorator
const name = options.name || propName;
if (options.serialize) {
target[name] = options.serialize(this, propValue);
}
else {
target[name] = propValue;
}
});
return target;
}
/**
* Deserializes an array by applying {@link deserialize} to each element.
* @template T the type of output object
* @param {I.Constructable<T>} ctor the destination constructor
* @param jsonArray the array to be deserialized
* @returns {T} the deserialized object
* @memberof JsonMapper
*/
deserializeArray(ctor, jsonArray) {
return jsonArray.map(v => this.deserialize(ctor, v));
}
/**
* Deserialization method.
* Deserializes `source` into an object built using `ctor`.
*
* Annotated properties are deserialized into a property using the `name` value as the source name (defaults to the property name).
*
* @template T the type of output object
* @param {I.Constructable<T>} ctor the destination constructor
* @param {*} source the value to be deserialized
* @param {(s: string) => any} stringParser the string parser to deserialize strings into JSON objects. Defaults to `JSON.parse`.
* @returns {T} the deserialized object
* @throws An error if a class encountered while deserializing has no {@link JsonClass} decorator.
* @memberof JsonMapper
*/
deserialize(ctor, source, stringParser = JSON.parse) {
const ctorOptions = (0,_reflection__WEBPACK_IMPORTED_MODULE_1__.getMetadata)(_interfaces__WEBPACK_IMPORTED_MODULE_0__.Symbols.mappingOptions, ctor);
if (!ctorOptions) {
throw new Error(`Class ${ctor.name} is not decorated with @JsonClass`);
}
// automatic parse of strings
if (typeof source === 'string') {
source = stringParser(source);
}
const target = new ctor();
const has = Object.prototype.hasOwnProperty;
const { ignoreUndecoratedProperties } = ctorOptions;
// keep track of mapped properties, so we can copy not mapped ones if "ignoreUndecoratedProperties" is false
const mapped = new Set();
// extract the property names array from the metadata stored in the constructor
// be careful: undecorated properties are NOT stored in this array
const propNames = (0,_reflection__WEBPACK_IMPORTED_MODULE_1__.getMetadata)(_interfaces__WEBPACK_IMPORTED_MODULE_0__.Symbols.fieldsMetadata, ctor) ?? [];
propNames.forEach(propName => {
const options = (0,_reflection__WEBPACK_IMPORTED_MODULE_1__.getMetadata)(_interfaces__WEBPACK_IMPORTED_MODULE_0__.Symbols.mappingMetadata, ctor, propName);
/* istanbul ignore next */
if (options === undefined) {
return;
}
const name = options.name || propName;
if (!has.call(source, name)) {
return;
}
if (options.deserialize) {
target[propName] = options.deserialize(this, source[name]);
}
else {
target[propName] = source[name];
}
mapped.add(name);
});
if (!ignoreUndecoratedProperties) {
// iterate ALL object keys (even undecorated ones, since we are using Object.keys)
Object.keys(source).forEach(propName => {
// copy over not mapped properties
// maybe here a clone should be performed?
if (!mapped.has(propName)) {
target[propName] = source[propName];
}
});
}
// call eventual after deserialize callback to post-process values
if (_interfaces__WEBPACK_IMPORTED_MODULE_0__.hasAfterDeserialize(target)) {
target.afterDeserialize();
}
return target;
}
}
/**
* This method checks if the input value is a {@link CustomSerialize} implementation.
* If it is so, it calls the custom export function and returns its output in the `value` field
* of the response.
*
* @param {JsonMapper} mapper the mapper
* @param mapValue the value to export
* @returns the exported value decorated with a boolean
*/
function exportCustom(mapper, mapValue) {
if (_interfaces__WEBPACK_IMPORTED_MODULE_0__.hasCustomSerializeExport(mapValue)) {
return {
serialized: true,
value: mapValue.customSerialize(mapper),
};
}
else {
return {
serialized: false,
};
}
}
/***/ })
/******/ ]);
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ })();
/******/
/******/ /* webpack/runtime/make namespace object */
/******/ (() => {
/******/ // define __esModule on exports
/******/ __webpack_require__.r = (exports) => {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/ })();
/******/
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ JsonArray: () => (/* reexport safe */ _public_api__WEBPACK_IMPORTED_MODULE_0__.JsonArray),
/* harmony export */ JsonArrayOfComplexProperty: () => (/* reexport safe */ _public_api__WEBPACK_IMPORTED_MODULE_0__.JsonArrayOfComplexProperty),
/* harmony export */ JsonClass: () => (/* reexport safe */ _public_api__WEBPACK_IMPORTED_MODULE_0__.JsonClass),
/* harmony export */ JsonComplexProperty: () => (/* reexport safe */ _public_api__WEBPACK_IMPORTED_MODULE_0__.JsonComplexProperty),
/* harmony export */ JsonMap: () => (/* reexport safe */ _public_api__WEBPACK_IMPORTED_MODULE_0__.JsonMap),
/* harmony export */ JsonMapper: () => (/* reexport safe */ _public_api__WEBPACK_IMPORTED_MODULE_0__.JsonMapper),
/* harmony export */ JsonProperty: () => (/* reexport safe */ _public_api__WEBPACK_IMPORTED_MODULE_0__.JsonProperty),
/* harmony export */ makeCustomDecorator: () => (/* reexport safe */ _public_api__WEBPACK_IMPORTED_MODULE_0__.makeCustomDecorator)
/* harmony export */ });
/* harmony import */ var _public_api__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
})();
var __webpack_export_target__ = exports;
for(var i in __webpack_exports__) __webpack_export_target__[i] = __webpack_exports__[i];
if(__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, "__esModule", { value: true });
/******/ })()
;
//# sourceMappingURL=index.cjs.map