cerializr
Version:
(de)serialization made easy with ES7/Typescript annotations (decorators)
194 lines (171 loc) • 4.99 kB
text/typescript
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 `<T>{ [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<T extends Indexable>(
source: T,
type: SerializableType<T>
): Indexable<JsonType> {
const target: Indexable<JsonType> = {};
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<T>(
source: Array<T>,
type: SerializableType<T>
): Array<JsonType> {
return source.map<JsonType>(val => Serialize(val, type));
}
export function SerializePrimitive<T>(
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<JsonType> = {};
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<T>(
instance: T,
type: SerializableType<T>
): 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<JsonType> = {};
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;
}
}