@openhps/core
Version:
Open Hybrid Positioning System - Core component
204 lines • 8.13 kB
JavaScript
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), {})
});
})();