@openhps/core
Version:
Open Hybrid Positioning System - Core component
263 lines • 15.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Serializer = void 0;
const serializer_1 = require("typedjson/lib/cjs/serializer");
const type_descriptor_1 = require("typedjson/lib/cjs/type-descriptor");
const typedjson_1 = require("typedjson");
const helpers_1 = require("typedjson/lib/cjs/helpers");
const options_base_1 = require("typedjson/lib/cjs/options-base");
const BufferUtils_1 = require("../utils/BufferUtils");
class Serializer extends serializer_1.Serializer {
constructor() {
super();
this.errorHandler = (e) => {
e.message = e.message.replace('@jsonObject', '@SerializableObject()');
e.message = e.message.replace('@jsonMember', '@SerializableMember()');
e.message = e.message.replace('@jsonSetMember', '@SerializableSetMember()');
e.message = e.message.replace('@jsonMapMember', '@SerializableMapMember()');
e.message = e.message.replace('@jsonArrayMember', '@SerializableArrayMember()');
throw e;
};
this.setSerializationStrategy(Map, this.convertAsMap.bind(this));
this.setSerializationStrategy(Array, this.convertAsArray.bind(this));
this.setSerializationStrategy(Set, this.convertAsSet.bind(this));
this.setSerializationStrategy(Uint8Array, BufferUtils_1.BufferUtils.toHexString);
}
convertSingleValue(sourceObject, typeDescriptor, memberName, memberOptions, serializerOptions) {
const targetObject = this._convertSingleValue.bind(this)(sourceObject, typeDescriptor, memberName, memberOptions, serializerOptions);
if (memberName === undefined && typeof targetObject === 'object') {
targetObject.__type = typeDescriptor.ctor.name;
}
return targetObject;
}
_convertSingleValue(sourceObject, typeDescriptor, memberName, memberOptions, serializerOptions) {
if (this.retrievePreserveNull(memberOptions) && sourceObject === null) {
return null;
}
if (!(0, helpers_1.isValueDefined)(sourceObject)) {
return;
}
if (!(0, helpers_1.isInstanceOf)(sourceObject, typeDescriptor.ctor)) {
const expectedName = (0, helpers_1.nameof)(typeDescriptor.ctor);
const actualName = (0, helpers_1.nameof)(sourceObject.constructor);
this.errorHandler(new TypeError(`Could not serialize '${memberName}': expected '${expectedName}',` + ` got '${actualName}'.`));
return;
}
const serializer = this.serializationStrategy.get(typeDescriptor.ctor);
if (serializer !== undefined) {
return serializer(sourceObject, typeDescriptor, memberName, this, memberOptions, serializerOptions);
}
// if not present in the strategy do property by property serialization
if (typeof sourceObject === 'object') {
return this.convertAsObject(sourceObject, typeDescriptor, memberName, this, memberOptions, serializerOptions);
}
let error = `Could not serialize '${memberName}'; don't know how to serialize type`;
if (typeDescriptor.hasFriendlyName()) {
error += ` '${typeDescriptor.ctor.name}'`;
}
this.errorHandler(new TypeError(`${error}.`));
}
convertAsObject(sourceObject, typeDescriptor, memberName, serializer, memberOptions, serializerOptions) {
let sourceTypeMetadata;
let targetObject;
let typeHintEmitter = serializer.getTypeHintEmitter();
if (sourceObject.constructor !== typeDescriptor.ctor && sourceObject instanceof typeDescriptor.ctor) {
// The source object is not of the expected type, but it is a valid subtype.
// This is OK, and we'll proceed to gather object metadata from the subtype instead.
sourceTypeMetadata = typedjson_1.JsonObjectMetadata.getFromConstructor(sourceObject.constructor);
}
else {
sourceTypeMetadata = typedjson_1.JsonObjectMetadata.getFromConstructor(typeDescriptor.ctor);
}
if (sourceTypeMetadata === undefined) {
// Untyped serialization, "as-is", we'll just pass the object on.
// We'll clone the source object, because type hints are added to the object itself, and we
// don't want to modify
// to the original object.
targetObject = Object.assign({}, sourceObject);
}
else {
const beforeSerializationMethodName = sourceTypeMetadata.beforeSerializationMethodName;
if (beforeSerializationMethodName != null) {
if (typeof sourceObject[beforeSerializationMethodName] === 'function') {
// check for member first
sourceObject[beforeSerializationMethodName]();
}
else if (typeof sourceObject.constructor[beforeSerializationMethodName] === 'function') {
// check for static
sourceObject.constructor[beforeSerializationMethodName]();
}
else {
serializer.getErrorHandler()(new TypeError(`beforeSerialization callback '` +
`${(0, helpers_1.nameof)(sourceTypeMetadata.classType)}.${beforeSerializationMethodName}` +
`' is not a method.`));
}
}
const sourceMeta = sourceTypeMetadata;
// Strong-typed serialization available.
// We'll serialize by members that have been marked with @jsonMember (including
// array/set/map members), and perform recursive conversion on each of them. The converted
// objects are put on the 'targetObject', which is what will be put into 'JSON.stringify'
// finally.
targetObject = {};
const classOptions = (0, options_base_1.mergeOptions)(serializer.options, sourceMeta.options);
if (sourceMeta.typeHintEmitter != null) {
typeHintEmitter = sourceMeta.typeHintEmitter;
}
sourceMeta.dataMembers.forEach((objMemberMetadata) => {
const objMemberOptions = (0, options_base_1.mergeOptions)(classOptions, objMemberMetadata.options);
let serialized;
const value = sourceObject[objMemberMetadata.key];
if (objMemberMetadata.serializer != null) {
serialized = objMemberMetadata.serializer(value, {
fallback: (so, td) => serializer.convertSingleValue(so, (0, type_descriptor_1.ensureTypeDescriptor)(td)),
});
}
else if (objMemberMetadata.type == null) {
throw new TypeError(`Could not serialize ${objMemberMetadata.name}, there is` +
` no constructor nor serialization function to use.`);
}
else if (objMemberMetadata.type() === typedjson_1.AnyT && value !== null && value !== undefined) {
// Any type, serialize as an unknown object
const globalDataType = Object.getPrototypeOf(value).constructor;
serialized = serializer.convertSingleValue(value, globalDataType ? (0, type_descriptor_1.ensureTypeDescriptor)(globalDataType) : objMemberMetadata.type(), `${(0, helpers_1.nameof)(sourceMeta.classType)}.${objMemberMetadata.key}`, objMemberOptions, serializerOptions);
if (typeof serialized === 'object' && serialized !== null) {
serialized.__type = globalDataType ? globalDataType.name : 'Object';
}
}
else {
serialized = serializer.convertSingleValue(value, objMemberMetadata.type(), `${(0, helpers_1.nameof)(sourceMeta.classType)}.${objMemberMetadata.key}`, objMemberOptions, serializerOptions);
}
if ((serializer.retrievePreserveNull(objMemberOptions) && serialized === null) ||
(0, helpers_1.isValueDefined)(serialized)) {
targetObject[objMemberMetadata.name] = serialized;
}
});
}
// Add type-hint.
typeHintEmitter(targetObject, sourceObject, typeDescriptor.ctor, sourceTypeMetadata);
return targetObject;
}
/**
* Performs the conversion of an array of typed objects (or primitive values) to an array of simple
* javascript objects
* (or primitive values) for serialization.
* @param sourceObject Source object to convert
* @param typeDescriptor Type descriptor of source object
* @param memberName Member name to convert
* @param serializer Serializer
* @param memberOptions Member options of memberName
* @param serializerOptions Custom serializer options
*/
convertAsArray(sourceObject, typeDescriptor, memberName, serializer, memberOptions, serializerOptions) {
if (!(typeDescriptor instanceof type_descriptor_1.ArrayTypeDescriptor)) {
throw new TypeError(`Could not serialize ${memberName} as Array: incorrect TypeDescriptor detected, please` +
' use proper annotation or function for this type');
}
if (typeDescriptor.elementType == null) {
throw new TypeError(`Could not serialize ${memberName} as Array: missing element type definition.`);
}
// Check the type of each element, individually.
// If at least one array element type is incorrect, we return undefined, which results in no
// value emitted during serialization. This is so that invalid element types don't unexpectedly
// alter the ordering of other, valid elements, and that no unexpected undefined values are in
// the emitted array.
sourceObject.forEach((element, i) => {
if (!(serializer.retrievePreserveNull(memberOptions) && element === null) &&
!(0, helpers_1.isInstanceOf)(element, typeDescriptor.elementType.ctor)) {
const expectedTypeName = (0, helpers_1.nameof)(typeDescriptor.elementType.ctor);
const actualTypeName = element && (0, helpers_1.nameof)(element.constructor);
throw new TypeError(`Could not serialize ${memberName}[${i}]:` +
` expected '${expectedTypeName}', got '${actualTypeName}'.`);
}
});
return sourceObject.map((element, i) => {
return serializer.convertSingleValue(element, typeDescriptor.elementType, `${memberName}[${i}]`, memberOptions, serializerOptions);
});
}
/**
* Performs the conversion of a set of typed objects (or primitive values) into an array
* of simple javascript objects.
* @param sourceObject
* @param typeDescriptor
* @param memberName
* @param serializer
* @param memberOptions
* @param serializerOptions
* @returns
*/
convertAsSet(sourceObject, typeDescriptor, memberName, serializer, memberOptions, serializerOptions) {
if (!(typeDescriptor instanceof type_descriptor_1.SetTypeDescriptor)) {
throw new TypeError(`Could not serialize ${memberName} as Set: incorrect TypeDescriptor detected, please` +
' use proper annotation or function for this type');
}
if (typeDescriptor.elementType == null) {
throw new TypeError(`Could not serialize ${memberName} as Set: missing element type definition.`);
}
memberName += '[]';
const resultArray = [];
// Convert each element of the set, and put it into an output array.
// The output array is the one serialized, as JSON.stringify does not support Set serialization.
// (TODO: clarification needed)
sourceObject.forEach((element) => {
const resultElement = serializer.convertSingleValue(element, typeDescriptor.elementType, memberName, memberOptions, serializerOptions);
// Add to output if the source element was undefined, OR the converted element is defined.
// This will add intentionally undefined values to output, but not values that became
// undefined DURING serializing (usually because of a type-error).
if (!(0, helpers_1.isValueDefined)(element) || (0, helpers_1.isValueDefined)(resultElement)) {
resultArray.push(resultElement);
}
});
return resultArray;
}
/**
* Performs the conversion of a map of typed objects (or primitive values) into an array
* of simple javascript objects with `key` and `value` properties.
* @param sourceObject
* @param typeDescriptor
* @param memberName
* @param serializer
* @param memberOptions
* @param serializerOptions
*/
convertAsMap(sourceObject, typeDescriptor, memberName, serializer, memberOptions, serializerOptions) {
if (!(typeDescriptor instanceof type_descriptor_1.MapTypeDescriptor)) {
// Serialize as object
return serializer.convertAsObject(sourceObject, typeDescriptor, memberName, serializer, memberOptions);
}
if (typeDescriptor.valueType == null) {
// @todo Check type
throw new TypeError(`Could not serialize ${memberName} as Map: missing value type definition.`);
}
if (typeDescriptor.keyType == null) {
// @todo Check type
throw new TypeError(`Could not serialize ${memberName} as Map: missing key type definition.`);
}
const keyMemberName = `${memberName}[].key`;
const valueMemberName = `${memberName}[].value`;
const resultShape = typeDescriptor.getCompleteOptions().shape;
const result = resultShape === 1 ? {} : [];
const preserveNull = serializer.retrievePreserveNull(memberOptions);
// Convert each *entry* in the map to a simple javascript object with key and value properties.
sourceObject.forEach((value, key) => {
const resultKeyValuePairObj = {
key: serializer.convertSingleValue(key, typeDescriptor.keyType, keyMemberName, memberOptions, serializerOptions),
value: serializer.convertSingleValue(value, typeDescriptor.valueType, valueMemberName, memberOptions, serializerOptions),
};
// We are not going to emit entries with undefined keys OR undefined values.
const keyDefined = (0, helpers_1.isValueDefined)(resultKeyValuePairObj.key);
const valueDefined = (resultKeyValuePairObj.value === null && preserveNull) || (0, helpers_1.isValueDefined)(resultKeyValuePairObj.value);
if (keyDefined && valueDefined) {
if (resultShape === 1) {
result[resultKeyValuePairObj.key] = resultKeyValuePairObj.value;
}
else {
result.push(resultKeyValuePairObj);
}
}
});
return result;
}
}
exports.Serializer = Serializer;
//# sourceMappingURL=Serializer.js.map