import { Indexable, JsonArray, JsonObject, JsonType, SerializablePrimitiveType, SerializableType, InstantiationMethod, } from "./interfaces"; import { getTarget, isPrimitiveType } from "./util"; import { MetaData, MetaDataFlag } from "./meta_data"; // import isNil from "lodash/isNil"; // import isUndefined from "lodash/isUndefined"; // import isObject from "lodash/isObject"; // import isFunction from "lodash/isFunction"; import { default as _ } from "./my-lodash"; export function DeserializeMap( data: JsonObject, type: SerializableType, target?: Indexable, instantiationMethod?: InstantiationMethod ): Indexable | null { if (_.isUndefined(instantiationMethod)) { instantiationMethod = MetaData.deserializeInstantationMethod; } if (typeof data !== "object") { throw new Error( "Expected input to be of type `object` but received: " + typeof data ); } if (_.isNil(target)) target = {}; if (_.isNil(data)) { return null; } const keys = Object.keys(data); for (const key of keys) { const value = data[key]; if (!_.isUndefined(value)) { target[MetaData.deserializeKeyTransform(key)] = _Deserialize( data[key] as JsonObject, type, target[key], instantiationMethod ) as T; } } return target; } export function DeserializeArray( data: JsonArray, type: SerializableType, target?: Array, instantiationMethod?: InstantiationMethod ) { if (_.isUndefined(instantiationMethod)) { instantiationMethod = MetaData.deserializeInstantationMethod; } if (!Array.isArray(data)) { throw new Error( "Expected input to be an array but received: " + typeof data ); } if (!Array.isArray(target)) target = new Array(); target = data.map( (val, i) => _Deserialize( val as JsonObject, type, target![i], instantiationMethod ) as T ); return target; } function DeserializePrimitive( data: any, type: SerializablePrimitiveType, target?: Date ) { if (type === Date) { const deserializedDate = new Date(data as string); if (target instanceof Date) { target.setTime(deserializedDate.getTime()); } else { return deserializedDate; } } else if (type === RegExp) { const fragments = data.match(/\/(.*?)\/([gimy])?$/); return new RegExp(fragments[1], fragments[2] || ""); } else if (data === null) { return null; } else { return (type as any)(data); } } export function DeserializeJSON( data: JsonType, transformKeys = true, target?: JsonType ): JsonType { if (Array.isArray(data)) { if (!Array.isArray(target)) target = new Array(data.length); target = data.map((val, i) => DeserializeJSON(val, transformKeys, (target as Array)[i]) ); return target; } if (_.isObject(data)) { const retn = (_.isObject(target) ? target : {}) as Indexable; const keys = Object.keys(data); for (const key of keys) { const value = (data as Indexable)[key]; if (!_.isUndefined(value)) { const retnKey = transformKeys ? MetaData.deserializeKeyTransform(key) : key; retn[retnKey] = DeserializeJSON( (data as Indexable)[key], transformKeys ); } } return retn; } else if (_.isFunction(data)) { throw new Error( "Cannot deserialize a function, input is not a valid json object" ); } //primitive case return data; } function _Deserialize( data: JsonObject, type: SerializableType, target?: T, instantiationMethod?: InstantiationMethod ): T | null { if (data === null) { return null; } const metadataList = MetaData.getMetaDataForType(type); if (metadataList === null) { if (typeof type === "function") { if (isPrimitiveType(type)) { return DeserializePrimitive( data, (type as unknown) as SerializablePrimitiveType, target as Date | undefined ); } switch (instantiationMethod) { case InstantiationMethod.New: return new type(); case InstantiationMethod.ObjectCreate: return Object.create(type.prototype); default: return {} as T; } } return null; } target = getTarget(type, target, instantiationMethod); for (let i = 0; i < metadataList.length; i++) { const metadata = metadataList[i]; if (metadata.deserializedKey === null) continue; const source: any = data[ metadata.transformKey ? metadata.getDeserializedKey() : metadata.deserializedKey ]; if (_.isUndefined(source)) continue; let keyName = metadata.keyName; const flags = metadata.flags; if (target) { if ((flags & MetaDataFlag.DeserializeMap) !== 0) { (target as Indexable)[keyName] = DeserializeMap( source, metadata.deserializedType, target[keyName], instantiationMethod ); } else if ((flags & MetaDataFlag.DeserializeArray) !== 0) { (target as Indexable)[keyName] = DeserializeArray( source, metadata.deserializedType, (target as Indexable)[keyName], instantiationMethod ); } else if ((flags & MetaDataFlag.DeserializePrimitive) !== 0) { (target as Indexable)[keyName] = DeserializePrimitive( source, metadata.deserializedType as SerializablePrimitiveType, target[keyName] ); } else if ((flags & MetaDataFlag.DeserializeObject) !== 0) { (target as Indexable)[keyName] = _Deserialize( source, metadata.deserializedType, target[keyName], instantiationMethod ); } else if ((flags & MetaDataFlag.DeserializeJSON) !== 0) { (target as Indexable)[keyName] = DeserializeJSON( source, metadata.transformKey, instantiationMethod ); } else if ((flags & MetaDataFlag.DeserializeUsing) !== 0) { (target as Indexable)[ keyName ] = (metadata.deserializedType as any)( source, target[keyName], instantiationMethod ); } } } if (_.isFunction(type.onDeserialized)) { const value = type.onDeserialized( data, target as T, instantiationMethod ); if (value !== void 0) return value as any; } return target as T; } export function Deserialize( data: JsonObject, type: SerializableType, target?: T, instantiationMethod?: InstantiationMethod ): T | null { if (_.isUndefined(instantiationMethod)) { instantiationMethod = MetaData.deserializeInstantationMethod; } return _Deserialize(data, type, target, instantiationMethod); } export function DeserializeRaw( data: JsonObject, type: SerializableType, target?: T ): T | null { return _Deserialize(data, type, target, InstantiationMethod.None); } export function DeserializeArrayRaw( data: JsonArray, type: SerializableType, target?: Array ): Array | null { return DeserializeArray(data, type, target, InstantiationMethod.None); } export function DeserializeMapRaw( data: Indexable, type: SerializableType, target?: Indexable ): Indexable | null { return DeserializeMap(data, type, target, InstantiationMethod.None); }