import { Indexable, JsonObject, JsonType, SerializablePrimitiveType, SerializableType, } from "./interfaces"; import { isPrimitiveType } from "./util"; import { MetaData, MetaDataFlag } from "./meta_data"; import { default as _ } from "./my-lodash"; import isObjectLike from "lodash/isObjectLike"; /** * takes an indexable object ie `{ [idx: string] : T }` and for each key serializes the object using the provided class type. * @param source - an indexable object * @param type - Type to serialize * * @return Indexable object of JsonType */ export function SerializeMap( source: T, type: SerializableType ): Indexable { const target: Indexable = {}; const keys = Object.keys(source); keys.forEach(key => { const value = source[key]; if (!_.isUndefined(value)) { target[MetaData.serializeKeyTransform(key)] = Serialize( value, type ); } }); return target; } /** * takes an array of objects and serializes each entry using the provided class type * * @param source - An array of objects * @param type - Type to serialize each entry of the array * * @return Array of JsonType */ export function SerializeArray( source: Array, type: SerializableType ): Array { return source.map(val => Serialize(val, type)); } export function SerializePrimitive( source: SerializablePrimitiveType, type: SerializablePrimitiveType ): JsonType { if (_.isNil(source)) { return null; } switch (type) { case Boolean: return Boolean(source); case Number: const value = Number(source); if (isNaN(value)) return null; return value; case String: case Date: case RegExp: default: return source.toString(); } } /** * takes any value and serializes it as json, no structure is assumed and any serialization annotations on any processed objects are totally ignored. * @param source * @param transformKeys * * @return JsonType */ export function SerializeJSON(source: any, transformKeys = true): JsonType { if (_.isNil(source)) return null; if (Array.isArray(source)) { return source.map(val => SerializeJSON(val, transformKeys)); } if (isObjectLike(source)) { if (_.isDate(source) || _.isRegExp(source)) { return source.toString(); } else { const sourceIndexed: Indexable = {}; const keys = Object.keys(source); keys.forEach(key => { const value = source[key]; if (!_.isUndefined(value)) { const sourceIndexedKey = transformKeys ? MetaData.serializeKeyTransform(key) : key; sourceIndexed[sourceIndexedKey] = SerializeJSON( value, transformKeys ); } }); return sourceIndexed; } } else if (_.isFunction(source)) { return null; } return source; } /** * takes a single object and serializes it using the provided class type. * @param instance - A single object to serialize * @param type - Type to serialize the instance * * @return A JsonObject or null */ export function Serialize( instance: T, type: SerializableType ): JsonObject | null { if (_.isNil(instance)) { return null; } const metadataList = MetaData.getMetaDataForType(type); // todo -- maybe move this to a Generic deserialize if (metadataList === null) { return isPrimitiveType(type) ? (SerializePrimitive( (instance as unknown) as SerializablePrimitiveType, type as any ) as JsonObject) : {}; } const target: Indexable = {}; metadataList.forEach(metadata => { if (metadata.serializedKey === null) return; const source = (instance as any)[metadata.keyName]; if (_.isUndefined(source)) return; const keyName = metadata.getSerializedKey(); target[keyName] = serializeByFlag(source, metadata); }); if (_.isFunction(type.onSerialized)) { const value = type.onSerialized(target, instance); if (!_.isUndefined(value)) { return value as JsonObject; } } return target; } function serializeByFlag(source: any, metadata: MetaData): JsonType { const flags = metadata.flags; switch (true) { case !!(flags & MetaDataFlag.SerializeMap): return SerializeMap(source, metadata.serializedType); case !!(flags & MetaDataFlag.SerializeArray): return SerializeArray(source, metadata.serializedType); case !!(flags & MetaDataFlag.SerializePrimitive): return SerializePrimitive( source, metadata.serializedType as SerializablePrimitiveType ); case !!(flags & MetaDataFlag.SerializeObject): return Serialize(source, metadata.serializedType); case !!(flags & MetaDataFlag.SerializeJSON): return SerializeJSON(source, metadata.transformKey); case !!(flags & MetaDataFlag.SerializeUsing): return (metadata.serializedType as any)(source); default: return null; } }