UNPKG

@openhps/core

Version:

Open Hybrid Positioning System - Core component

204 lines 8.13 kB
import { EventEmitter } from 'events'; import { JsonObjectMetadata, jsonObject } from 'typedjson'; import { DataSerializerUtils } from './DataSerializerUtils'; import { Deserializer } from './Deserializer'; import { Serializer } from './Serializer'; import { SerializableMember } from './decorators/SerializableMember'; JsonObjectMetadata.getFromConstructor = function (ctor) { if (!ctor) { return; } const prototype = ctor.prototype; if (prototype == null) { return; } let metadata; if (Object.prototype.hasOwnProperty.call(prototype, DataSerializerUtils.META_FIELD)) { // The class prototype contains own jsonObject metadata metadata = prototype[DataSerializerUtils.META_FIELD]; } else { const parent = Object.getPrototypeOf(ctor.prototype); if (!parent) { return; } metadata = JsonObjectMetadata.getFromConstructor(parent.constructor); } // Ignore implicitly added jsonObject (through jsonMember) if ((metadata === null || metadata === void 0 ? void 0 : metadata.isExplicitlyMarked) === true) { return metadata; } // In the end maybe it is something which we can handle directly if (JsonObjectMetadata['doesHandleWithoutAnnotation'](ctor)) { const primitiveMeta = new JsonObjectMetadata(ctor); primitiveMeta.isExplicitlyMarked = true; // we do not store the metadata here to not modify builtin prototype return primitiveMeta; } }; /** * Allows the serialization and deserialization of objects using the {@link SerializableObject} decorator. * * ## Usage * * ### Registration * Objects are registered upon loading with the {@link SerializableObject} decorator. * Manual registration is possible using: * ```typescript * DataSerializer.registerType(MyObjectClass); * ``` */ export class DataSerializer { /** * Manually register a new type * @param {typeof any} type Type to register * @param {MappedTypeConverters} [converters] Optional converters */ static registerType(type, converters) { DataSerializer.knownTypes.set(type.name, type); if (converters) { // Set custom serialization strategies if (converters.serializer) { DataSerializer.serializer.setSerializationStrategy(type, value => { return converters.serializer(value, { fallback: (so, td) => DataSerializer.serializer.convertSingleValue(so, td) }); }); } if (converters.deserializer) { DataSerializer.deserializer.setDeserializationStrategy(type, value => { return converters.deserializer(value, { fallback: (so, td) => DataSerializer.deserializer.convertSingleValue(so, td, DataSerializer.knownTypes) }); }); } if (type.name !== 'Object') { // Ensure that the type has a serializable metadata DataSerializerUtils.createMetadata(type.prototype); const options = {}; jsonObject(options)(type); DataSerializer['eventEmitter'].emit('updateSerializableObject', type, options); // Merge const ownMeta = DataSerializerUtils.getMetadata(type); const rootMeta = DataSerializerUtils.getRootMetadata(type.prototype); DataSerializerUtils.updateObjectMetadata(type, options, ownMeta, rootMeta); } // Register members if (converters.members) { Object.keys(converters.members).forEach(key => { const memberOptions = converters.members[key]; SerializableMember(memberOptions)(type.prototype, key); }); } } DataSerializer.eventEmitter.emit('registerType', type, converters); } /** * Get the TypedJSON metadata * @deprecated use {@link DataSerializerUtils.getMetadata} * @see {@link https://gist.github.com/krizka/c83fb1966dd57997a1fc02625719387d} * @param {any} proto Prototype of target * @returns {ObjectMetadata} Root object metadata */ static getMetadata(proto) { return DataSerializerUtils.getMetadata(proto); } /** * Get the root TypedJSON metadata * @deprecated use {@link DataSerializerUtils.getRootMetadata} * @see {@link https://gist.github.com/krizka/c83fb1966dd57997a1fc02625719387d} * @param {any} proto Prototype of target * @returns {ObjectMetadata} Root object metadata */ static getRootMetadata(proto) { return DataSerializerUtils.getRootMetadata(proto); } /** * Find the root TypedJSON metadata * @deprecated use {@link DataSerializerUtils.getRootMetadata} * @param {any} proto Prototype of target * @returns {ObjectMetadata} Root object metadata */ static findRootMetaInfo(proto) { return DataSerializerUtils.getRootMetadata(proto); } /** * Unregister a type * @param {typeof any} type Type to unregister */ static unregisterType(type) { DataSerializer.knownTypes.delete(type.name); DataSerializer.eventEmitter.emit('unregisterType', type); } static findTypeByName(name) { return DataSerializer.knownTypes.get(name); } /** * Clone a serializable object * @param {any} object Serializable object * @param {Constructor<any>} [dataType] Data type to clone to * @returns {any} Cloned object */ static clone(object, dataType) { return DataSerializer.deserialize(DataSerializer.serialize(object), dataType); } /** * Serialize data * @param {any} data Data to serialize * @param {DataSerializerConfig} [config] Data serializer configuration * @returns {any} Serialized data */ static serialize(data, config = {}) { var _a; if (data === null || data === undefined) { return undefined; } const globalDataType = Object.getPrototypeOf(data).constructor; // First check if it is a registered type // this is important as some serializable classes // may extend an array if (!DataSerializer.findTypeByName(globalDataType.name) && Array.isArray(data)) { return data.map(DataSerializer.serialize.bind(DataSerializer)); } const serializer = (_a = config.serializer) !== null && _a !== void 0 ? _a : DataSerializer.serializer; return serializer.convertSingleValue(data, DataSerializerUtils.ensureTypeDescriptor(globalDataType), undefined, undefined, config); } static deserialize(serializedData, dataType, config = {}) { var _a; if (typeof serializedData !== 'object' && typeof serializedData !== 'function' || !serializedData) { return serializedData; } if (Array.isArray(serializedData)) { return serializedData.map(serializedObject => DataSerializer.deserialize(serializedObject)); } const deserializer = (_a = config.deserializer) !== null && _a !== void 0 ? _a : DataSerializer.deserializer; const finalType = dataType !== null && dataType !== void 0 ? dataType : deserializer.getTypeResolver()(serializedData, DataSerializer.knownTypes); return deserializer.convertSingleValue(serializedData, DataSerializerUtils.ensureTypeDescriptor(finalType), DataSerializer.knownTypes, undefined, undefined, config); } } DataSerializer.knownTypes = new Map(); DataSerializer.serializer = new Serializer(); DataSerializer.deserializer = new Deserializer(); /* Event emitter used to listen for registrations and unregister of data types */ DataSerializer.eventEmitter = new EventEmitter(); (() => { DataSerializer.registerType(Object, { serializer: object => Object.assign(Object.assign({}, Object.keys(object).map(key => { return { [key]: typeof object[key] === 'function' ? { function: object[key].toString(), __type: 'Function' } : DataSerializer.serialize(object[key]) }; }).reduce((a, b) => Object.assign(Object.assign({}, a), b), {})), { __type: 'Object' }), deserializer: objectJson => Object.keys(objectJson).map(key => { if (key === '__type') { return {}; } return { [key]: typeof objectJson[key] === 'object' && objectJson[key].__type === 'Function' ? eval(objectJson[key].function) : DataSerializer.deserialize(objectJson[key]) }; }).reduce((a, b) => Object.assign(Object.assign({}, a), b), {}) }); })();