cerialize
Version:
Easy serialization throught ES7/Typescript annotations
823 lines (718 loc) • 31.7 kB
text/typescript
//this library can be used in Node or a browser, make sure our global object points to the right place
declare var global : any;
var win : any = null;
try {
win = window;
}
catch (e) {
win = global;
}
//some other modules might want access to the serialization meta data, expose it here
var TypeMap = win.__CerializeTypeMap = new (win as any).Map();
//type aliases for serialization functions
export type Serializer = (value : any) => any;
export type Deserializer = (value : any) => any;
export interface INewable<T> {
new (...args : any[]) : T;
}
export interface IEnum {
[enumeration : string] : any
}
export interface ISerializable {
Serialize? : (value : any) => any;
Deserialize? : (json : any, instance? : any) => any;
}
//convert strings like my_camel_string to myCamelString
export function CamelCase(str : string) : string {
var STRING_CAMELIZE_REGEXP = (/(\-|_|\.|\s)+(.)?/g);
return str.replace(STRING_CAMELIZE_REGEXP, function (match, separator, chr) : string {
return chr ? chr.toUpperCase() : '';
}).replace(/^([A-Z])/, function (match, separator, chr) : string {
return match.toLowerCase();
});
}
//convert strings like MyCamelString to my_camel_string
export function SnakeCase(str : string) : string {
var STRING_DECAMELIZE_REGEXP = (/([a-z\d])([A-Z])/g);
return str.replace(STRING_DECAMELIZE_REGEXP, '$1_$2').toLowerCase();
}
//convert strings like myCamelCase to my_camel_case
export function UnderscoreCase(str : string) : string {
var STRING_UNDERSCORE_REGEXP_1 = (/([a-z\d])([A-Z]+)/g);
var STRING_UNDERSCORE_REGEXP_2 = (/\-|\s+/g);
return str.replace(STRING_UNDERSCORE_REGEXP_1, '$1_$2').replace(STRING_UNDERSCORE_REGEXP_2, '_').toLowerCase();
}
//convert strings like my_camelCase to my-camel-case
export function DashCase(str : string) : string {
var STRING_DASHERIZE_REGEXP = (/([a-z\d])([A-Z])/g);
str = str.replace(/_/g, '-');
return str.replace(STRING_DASHERIZE_REGEXP, '$1-$2').toLowerCase();
}
function deserializeString(value : any) : string|string[] {
if (Array.isArray(value)) {
return value.map(function (element : any) {
return element && element.toString() || null;
});
}
else {
return value && value.toString() || null;
}
}
function deserializeNumber(value : any) : number|number[] {
if(Array.isArray(value)) {
return value.map(function(element : any) {
return parseFloat(element);
});
}
else {
return parseFloat(value);
}
}
function deserializeBoolean(value : any) : boolean|boolean[] {
if (Array.isArray(value)) {
return value.map(function (element : any) {
return Boolean(element);
});
}
else {
return Boolean(value);
}
}
function serializeString(value : any) : string|string[] {
if (Array.isArray(value)) {
return value.map(function (element : any) {
return element && element.toString() || null;
});
}
else {
return value && value.toString() || null;
}
}
function serializeNumber(value : any) : number|number[] {
if (Array.isArray(value)) {
return value.map(function (element : any) {
return parseInt(element);
});
}
else {
return parseInt(value);
}
}
function serializeBoolean(value : any) : boolean|boolean[] {
if (Array.isArray(value)) {
return value.map(function (element : any) {
return Boolean(element);
});
}
else {
return Boolean(value);
}
}
function getDeserializeFnForType(type : any) : Deserializer {
if (type === String) {
return deserializeString;
}
else if (type === Number) {
return deserializeNumber;
}
else if (type === Boolean) {
return deserializeBoolean;
}
else {
return type;
}
}
function getSerializeFnForType(type : any) : Serializer {
if (type === String) {
return serializeString;
}
else if (type === Number) {
return serializeNumber;
}
else if (type === Boolean) {
return serializeBoolean;
}
else {
return type;
}
}
//gets meta data for a key name, creating a new meta data instance
//if the input array doesn't already define one for the given keyName
function getMetaData(array : Array<MetaData>, keyName : string) : MetaData {
for (var i = 0; i < array.length; i++) {
if (array[i].keyName === keyName) {
return array[i];
}
}
array.push(new MetaData(keyName));
return array[array.length - 1];
}
//helper for grabbing the type and keyname from a multi-type input variable
function getTypeAndKeyName(keyNameOrType : string|Function|ISerializable, keyName : string) : {type : Function, key : string} {
var type : Function = null;
var key : string = null;
if (typeof keyNameOrType === "string") {
key = <string>keyNameOrType;
} else if (keyNameOrType && typeof keyNameOrType === "function" || typeof keyNameOrType === "object") {
type = <Function>keyNameOrType;
key = keyName;
}
return {key: key, type: type};
}
//todo instance.constructor.prototype.__proto__ === parent class, maybe use this?
//because types are stored in a JS Map keyed by constructor, serialization is not inherited by default
//keeping this seperate by default also allows sub classes to serialize differently than their parent
export function inheritSerialization(parentType : Function) : any {
return function (childType : Function) {
var parentMetaData : Array<MetaData> = TypeMap.get(parentType) || [];
var childMetaData : Array<MetaData> = TypeMap.get(childType) || [];
for (var i = 0; i < parentMetaData.length; i++) {
var keyName = parentMetaData[i].keyName;
if (!MetaData.hasKeyName(childMetaData, keyName)) {
childMetaData.push(MetaData.clone(parentMetaData[i]));
}
}
TypeMap.set(childType, childMetaData);
}
}
//an untyped serialization property annotation, gets existing meta data for the target or creates
//a new one and assigns the serialization key for that type in the meta data
export function serialize(target : any, keyName : string) : any {
if (!target || !keyName) return;
var metaDataList : Array<MetaData> = TypeMap.get(target.constructor) || [];
var metadata = getMetaData(metaDataList, keyName);
metadata.serializedKey = keyName;
TypeMap.set(target.constructor, metaDataList);
}
//an untyped deserialization property annotation, gets existing meta data for the target or creates
//a new one and assigns the deserialization key for that type in the meta data
export function deserialize(target : any, keyName : string) : any {
if (!target || !keyName) return;
var metaDataList : Array<MetaData> = TypeMap.get(target.constructor) || [];
var metadata = getMetaData(metaDataList, keyName);
metadata.deserializedKey = keyName;
TypeMap.set(target.constructor, metaDataList);
}
//this combines @serialize and @deserialize as defined above
export function autoserialize(target : any, keyName : string) : any {
if (!target || !keyName) return;
var metaDataList : Array<MetaData> = TypeMap.get(target.constructor) || [];
var metadata = getMetaData(metaDataList, keyName);
metadata.serializedKey = keyName;
metadata.deserializedKey = keyName;
TypeMap.set(target.constructor, metaDataList);
}
//We dont actually need the type to serialize but I like the consistency with deserializeAs which definitely does
//serializes a type using 1.) a custom key name, 2.) a custom type, or 3.) both custom key and type
export function serializeAs(keyNameOrType : string|Serializer|INewable<any>|ISerializable|IEnum, keyName? : string) : any {
if (!keyNameOrType) return;
var {key, type} = getTypeAndKeyName(keyNameOrType, keyName);
return function (target : any, actualKeyName : string) : any {
if (!target || !actualKeyName) return;
var metaDataList : Array<MetaData> = TypeMap.get(target.constructor) || [];
var metadata = getMetaData(metaDataList, actualKeyName);
metadata.serializedKey = (key) ? key : actualKeyName;
metadata.serializedType = type;
//this allows the type to be a stand alone function instead of a class
if (type !== Date && type !== RegExp && !TypeMap.get(type) && typeof type === "function") {
metadata.serializedType = <ISerializable>{
Serialize: getSerializeFnForType(type)
};
}
TypeMap.set(target.constructor, metaDataList);
};
}
//Supports serializing of dictionary-like map objects, ie: { x: {}, y: {} }
export function serializeIndexable(type : Serializer|INewable<any>|ISerializable, keyName? : string) : any {
if (!type) return;
return function (target : any, actualKeyName : string) : any {
if (!target || !actualKeyName) return;
var metaDataList : Array<MetaData> = TypeMap.get(target.constructor) || [];
var metadata = getMetaData(metaDataList, actualKeyName);
metadata.serializedKey = (keyName) ? keyName : actualKeyName;
metadata.serializedType = type;
metadata.indexable = true;
//this allows the type to be a stand alone function instead of a class
if (type !== Date && type !== RegExp && !TypeMap.get(type) && typeof type === "function") {
metadata.serializedType = <ISerializable>{
Serialize: getSerializeFnForType(type)
};
}
TypeMap.set(target.constructor, metaDataList);
};
}
//deserializes a type using 1.) a custom key name, 2.) a custom type, or 3.) both custom key and type
export function deserializeAs(keyNameOrType : string|Function|INewable<any>|ISerializable|IEnum, keyName? : string) : any {
if (!keyNameOrType) return;
var {key, type} = getTypeAndKeyName(keyNameOrType, keyName);
return function (target : any, actualKeyName : string) : any {
if (!target || !actualKeyName) return;
var metaDataList : Array<MetaData> = TypeMap.get(target.constructor) || [];
var metadata = getMetaData(metaDataList, actualKeyName);
metadata.deserializedKey = (key) ? key : actualKeyName;
metadata.deserializedType = type;
//this allows the type to be a stand alone function instead of a class
//todo maybe add an explicit date and regexp deserialization function here
if (!TypeMap.get(type) && type !== Date && type !== RegExp && typeof type === "function") {
metadata.deserializedType = <ISerializable>{
Deserialize: getDeserializeFnForType(type)
};
}
TypeMap.set(target.constructor, metaDataList);
};
}
//Supports deserializing of dictionary-like map objects, ie: { x: {}, y: {} }
export function deserializeIndexable(type : Function|INewable<any>|ISerializable, keyName? : string) : any {
if (!type) return;
var key = keyName;
return function (target : any, actualKeyName : string) : any {
if (!target || !actualKeyName) return;
var metaDataList : Array<MetaData> = TypeMap.get(target.constructor) || [];
var metadata = getMetaData(metaDataList, actualKeyName);
metadata.deserializedKey = (key) ? key : actualKeyName;
metadata.deserializedType = type;
metadata.indexable = true;
if (!TypeMap.get(type) && type !== Date && type !== RegExp && typeof type === "function") {
metadata.deserializedType = <ISerializable>{
Deserialize: getDeserializeFnForType(type)
};
}
TypeMap.set(target.constructor, metaDataList);
};
}
//serializes and deserializes a type using 1.) a custom key name, 2.) a custom type, or 3.) both custom key and type
export function autoserializeAs(keyNameOrType : string|Function|INewable<any>|ISerializable|IEnum, keyName? : string) : any {
if (!keyNameOrType) return;
var {key, type} = getTypeAndKeyName(keyNameOrType, keyName);
return function (target : any, actualKeyName : string) : any {
if (!target || !actualKeyName) return;
var metaDataList : Array<MetaData> = TypeMap.get(target.constructor) || [];
var metadata = getMetaData(metaDataList, actualKeyName);
var serialKey = (key) ? key : actualKeyName;
metadata.deserializedKey = serialKey;
metadata.deserializedType = type;
metadata.serializedKey = serialKey;
metadata.serializedType = getSerializeFnForType(type);
if (!TypeMap.get(type) && type !== Date && type !== RegExp && typeof type === "function") {
metadata.deserializedType = <ISerializable>{
Deserialize: getDeserializeFnForType(type)
};
}
TypeMap.set(target.constructor, metaDataList);
};
}
//Supports serializing/deserializing of dictionary-like map objects, ie: { x: {}, y: {} }
export function autoserializeIndexable(type : Function|INewable<any>|ISerializable, keyName? : string) : any {
if (!type) return;
var key = keyName;
return function (target : any, actualKeyName : string) : any {
if (!target || !actualKeyName) return;
var metaDataList : Array<MetaData> = TypeMap.get(target.constructor) || [];
var metadata = getMetaData(metaDataList, actualKeyName);
var serialKey = (key) ? key : actualKeyName;
metadata.deserializedKey = serialKey;
metadata.deserializedType = type;
metadata.serializedKey = serialKey;
metadata.serializedType = getSerializeFnForType(type);
metadata.indexable = true;
if (!TypeMap.get(type) && type !== Date && type !== RegExp && typeof type === "function") {
metadata.deserializedType = <ISerializable>{
Deserialize: getDeserializeFnForType(type)
};
}
TypeMap.set(target.constructor, metaDataList);
};
}
//helper class to contain serialization meta data for a property, each property
//in a type tagged with a serialization annotation will contain an array of these
//objects each describing one property
class MetaData {
public keyName : string; //the key name of the property this meta data describes
public serializedKey : string; //the target keyname for serializing
public deserializedKey : string; //the target keyname for deserializing
public serializedType : Function|INewable<any>|ISerializable; //the type or function to use when serializing this property
public deserializedType : Function|INewable<any>|ISerializable; //the type or function to use when deserializing this property
public indexable : boolean;
constructor(keyName : string) {
this.keyName = keyName;
this.serializedKey = null;
this.deserializedKey = null;
this.deserializedType = null;
this.serializedType = null;
this.indexable = false;
}
//checks for a key name in a meta data array
public static hasKeyName(metadataArray : Array<MetaData>, key : string) : boolean {
for (var i = 0; i < metadataArray.length; i++) {
if (metadataArray[i].keyName === key) return true;
}
return false;
}
//clone a meta data instance, used for inheriting serialization properties
public static clone(data : MetaData) : MetaData {
var metadata = new MetaData(data.keyName);
metadata.deserializedKey = data.deserializedKey;
metadata.serializedKey = data.serializedKey;
metadata.serializedType = data.serializedType;
metadata.deserializedType = data.deserializedType;
metadata.indexable = data.indexable;
return metadata;
}
}
//merges two primitive objects recursively, overwriting or defining properties on
//`instance` as they defined in `json`. Works on objects, arrays and primitives
function mergePrimitiveObjects(instance : any, json : any) : any {
if (!json) return instance; //if we dont have a json value, just use what the instance defines already
if (!instance) return json; //if we dont have an instance value, just use the json
//for each key in the input json we need to do a merge into the instance object
Object.keys(json).forEach(function (key : string) {
var transformedKey : string = key;
if (typeof deserializeKeyTransform === "function") {
transformedKey = deserializeKeyTransform(key);
}
var jsonValue : any = json[key];
var instanceValue : any = instance[key];
if (Array.isArray(jsonValue)) {
//in the array case we reuse the items that exist already where possible
//so reset the instance array length (or make it an array if it isnt)
//then call mergePrimitiveObjects recursively
instanceValue = Array.isArray(instanceValue) ? instanceValue : [];
instanceValue.length = jsonValue.length;
for (var i = 0; i < instanceValue.length; i++) {
instanceValue[i] = mergePrimitiveObjects(instanceValue[i], jsonValue[i]);
}
}
else if (jsonValue && typeof jsonValue === "object") {
if (!instanceValue || typeof instanceValue !== "object") {
instanceValue = {};
}
instanceValue = mergePrimitiveObjects(instanceValue, jsonValue);
}
else {
//primitive case, just use straight assignment
instanceValue = jsonValue;
}
instance[transformedKey] = instanceValue;
});
return instance;
}
//takes an array defined in json and deserializes it into an array that ist stuffed with instances of `type`.
//any instances already defined in `arrayInstance` will be re-used where possible to maintain referential integrity.
function deserializeArrayInto(source : Array<any>, type : Function|INewable<any>|ISerializable, arrayInstance : any) : Array<any> {
if (!Array.isArray(arrayInstance)) {
arrayInstance = new Array<any>(source.length);
}
//extend or truncate the target array to match the source array
arrayInstance.length = source.length;
for (var i = 0; i < source.length; i++) {
arrayInstance[i] = DeserializeInto(source[i], type, arrayInstance[i] || new (<any>type)());
}
return arrayInstance;
}
//takes an object defined in json and deserializes it into a `type` instance or populates / overwrites
//properties on `instance` if it is provided.
function deserializeObjectInto(json : any, type : Function|INewable<any>|ISerializable, instance : any) : any {
var metadataArray : Array<MetaData> = TypeMap.get(type);
//if we dont have an instance we need to create a new `type`
if (instance === null || instance === void 0) {
if (type) {
instance = new (<any>type)();
}
}
//if we dont have any meta data and we dont have a type to inflate, just merge the objects
if (instance && !type && !metadataArray) {
return mergePrimitiveObjects(instance, json);
}
//if we dont have meta data just bail out and keep what we have
if (!metadataArray) {
invokeDeserializeHook(instance, json, type);
return instance;
}
//for each property in meta data, try to hydrate that property with its corresponding json value
for (var i = 0; i < metadataArray.length; i++) {
var metadata = metadataArray[i];
//these are not the droids we're looking for (to deserialize), moving along
if (!metadata.deserializedKey) continue;
var serializedKey = metadata.deserializedKey;
if (metadata.deserializedKey === metadata.keyName) {
if (typeof deserializeKeyTransform === "function") {
serializedKey = deserializeKeyTransform(metadata.keyName);
}
}
var source = json[serializedKey];
if (source === void 0) continue;
var keyName = metadata.keyName;
//if there is a custom deserialize function, use that
if (metadata.deserializedType && typeof (<any>metadata.deserializedType).Deserialize === "function") {
instance[keyName] = (<any>metadata.deserializedType).Deserialize(source);
}
//in the array case check for a type, if we have one deserialize an array full of those,
//otherwise (thanks to @garkin) just do a generic array deserialize
else if (Array.isArray(source)) {
if (metadata.deserializedType) {
instance[keyName] = deserializeArrayInto(source, metadata.deserializedType, instance[keyName]);
} else {
instance[keyName] = deserializeArray(source, null);
}
}
//if the type is a Date, reconstruct a real date instance
else if ((typeof source === "string" || source instanceof Date) && metadata.deserializedType === Date.prototype.constructor) {
var deserializedDate = new Date(source);
if (instance[keyName] instanceof Date) {
instance[keyName].setTime(deserializedDate.getTime());
}
else {
instance[keyName] = deserializedDate;
}
}
//if the type is Regexp, do that
else if (typeof source === "string" && type === RegExp) {
instance[keyName] = new RegExp(source);
}
//if source exists and source is an object type, hydrate that type
else if (source && typeof source === "object") {
if (metadata.indexable) {
instance[keyName] = deserializeIndexableObjectInto(source, metadata.deserializedType, instance[keyName]);
}
else {
instance[keyName] = deserializeObjectInto(source, metadata.deserializedType, instance[keyName]);
}
}
//primitive case, just copy
else {
instance[keyName] = source;
}
}
//invoke our after deserialized callback if provided
invokeDeserializeHook(instance, json, type);
return instance;
}
//deserializes a bit of json into a `type`
export function Deserialize(json : any, type? : Function|INewable<any>|ISerializable) : any {
if (Array.isArray(json)) {
return deserializeArray(json, type);
}
else if (json && typeof json === "object") {
return deserializeObject(json, type);
}
else if ((typeof json === "string" || json instanceof Date) && type === Date.prototype.constructor) {
return new Date(json);
}
else if (typeof json === "string" && type === RegExp) {
return new RegExp(<string>json);
}
else {
return json;
}
}
//takes some json, a type, and a target object and deserializes the json into that object
export function DeserializeInto(source : any, type : Function|INewable<any>|ISerializable, target : any) : any {
if (Array.isArray(source)) {
return deserializeArrayInto(source, type, target || []);
}
else if (source && typeof source === "object") {
return deserializeObjectInto(source, type, target || new (<any>type)());
}
else {
return target || (type && new (<any>type)()) || null;
}
}
//deserializes an array of json into an array of `type`
function deserializeArray(source : Array<any>, type : Function|INewable<any>|ISerializable) : Array<any> {
var retn : Array<any> = new Array(source.length);
for (var i = 0; i < source.length; i++) {
retn[i] = Deserialize(source[i], type);
}
return retn;
}
function invokeDeserializeHook(instance : any, json : any, type : any) : void {
if (type && typeof(type).OnDeserialized === "function") {
type.OnDeserialized(instance, json);
}
}
function invokeSerializeHook(instance : any, json : any) : void {
if (typeof (instance.constructor).OnSerialized === "function") {
(instance.constructor).OnSerialized(instance, json);
}
}
//deserialize a bit of json into an instance of `type`
function deserializeObject(json : any, type : Function|INewable<any>|ISerializable) : any {
var metadataArray : Array<MetaData> = TypeMap.get(type);
//if we dont have meta data, just decode the json and use that
if (!metadataArray) {
var inst : any = null;
if (!type) {
inst = JSON.parse(JSON.stringify(json));
}
else {
inst = new (<any>type)(); //todo this probably wrong
invokeDeserializeHook(inst, json, type);
}
return inst;
}
var instance = new (<any>type)();
//for each tagged property on the source type, try to deserialize it
for (var i = 0; i < metadataArray.length; i++) {
var metadata = metadataArray[i];
if (!metadata.deserializedKey) continue;
var serializedKey = metadata.deserializedKey;
if (metadata.deserializedKey === metadata.keyName) {
if (typeof deserializeKeyTransform === "function") {
serializedKey = deserializeKeyTransform(metadata.keyName);
}
}
var source = json[serializedKey];
var keyName = metadata.keyName;
if (source === void 0) continue;
if (source === null) {
instance[keyName] = source;
}
//if there is a custom deserialize function, use that
else if (metadata.deserializedType && typeof (<any>metadata.deserializedType).Deserialize === "function") {
instance[keyName] = (<any>metadata.deserializedType).Deserialize(source);
}
//in the array case check for a type, if we have one deserialize an array full of those,
//otherwise (thanks to @garkin) just do a generic array deserialize
else if (Array.isArray(source)) {
instance[keyName] = deserializeArray(source, metadata.deserializedType || null);
}
else if ((typeof source === "string" || source instanceof Date) && metadata.deserializedType === Date.prototype.constructor) {
instance[keyName] = new Date(source);
}
else if (typeof source === "string" && metadata.deserializedType === RegExp) {
instance[keyName] = new RegExp(json);
}
else if (source && typeof source === "object") {
if (metadata.indexable) {
instance[keyName] = deserializeIndexableObject(source, metadata.deserializedType);
}
else {
instance[keyName] = deserializeObject(source, metadata.deserializedType);
}
}
else {
instance[keyName] = source;
}
}
invokeDeserializeHook(instance, json, type);
return instance;
}
function deserializeIndexableObject(source : any, type : Function | ISerializable) : any {
var retn : any = {};
//todo apply key transformation here?
Object.keys(source).forEach(function (key : string) {
retn[key] = deserializeObject(source[key], type);
});
return retn;
}
function deserializeIndexableObjectInto(source : any, type : Function | ISerializable, instance : any) : any {
//todo apply key transformation here?
Object.keys(source).forEach(function (key : string) {
instance[key] = deserializeObjectInto(source[key], type, instance[key]);
});
return instance;
}
//take an array and spit out json
function serializeArray(source : Array<any>, type? : Function|ISerializable) : Array<any> {
var serializedArray : Array<any> = new Array(source.length);
for (var j = 0; j < source.length; j++) {
serializedArray[j] = Serialize(source[j], type);
}
return serializedArray;
}
//take an instance of something and try to spit out json for it based on property annotaitons
function serializeTypedObject(instance : any, type? : Function|ISerializable) : any {
var json : any = {};
var metadataArray : Array<MetaData>;
if (type) {
metadataArray = TypeMap.get(type);
} else {
metadataArray = TypeMap.get(instance.constructor);
}
for (var i = 0; i < metadataArray.length; i++) {
var metadata = metadataArray[i];
if (!metadata.serializedKey) continue;
var serializedKey = metadata.serializedKey;
if (metadata.serializedKey === metadata.keyName) {
if (typeof serializeKeyTransform === "function") {
serializedKey = serializeKeyTransform(metadata.keyName);
}
}
var source = instance[metadata.keyName];
if (source === void 0) continue;
if (Array.isArray(source)) {
json[serializedKey] = serializeArray(source, metadata.serializedType || null);
}
else if (metadata.serializedType && typeof (<any>metadata.serializedType).Serialize === "function") {
//todo -- serializeIndexableObject probably isn't needed because of how serialize works
json[serializedKey] = (<any>metadata.serializedType).Serialize(source);
} else {
var value = Serialize(source);
if (value !== void 0) {
json[serializedKey] = value;
}
}
}
invokeSerializeHook(instance, json);
return json;
}
//take an instance of something and spit out some json
export function Serialize(instance : any, type? : Function|ISerializable) : any {
if (instance === null || instance === void 0) return null;
if (Array.isArray(instance)) {
return serializeArray(instance, type);
}
if (type && TypeMap.has(type)) {
return serializeTypedObject(instance, type);
}
if (instance.constructor && TypeMap.has(instance.constructor)) {
return serializeTypedObject(instance);
}
if (instance instanceof Date) {
return instance.toISOString();
}
if (instance instanceof RegExp) {
return instance.toString();
}
if (instance && typeof instance === 'object' || typeof instance === 'function') {
var keys = Object.keys(instance);
var json : any = {};
for (var i = 0; i < keys.length; i++) {
//todo this probably needs a key transform
json[keys[i]] = Serialize(instance[keys[i]]);
}
invokeSerializeHook(instance, json);
return json;
}
return instance;
}
export function GenericDeserialize<T>(json : any, type : INewable<T>) : T {
return <T>Deserialize(json, type);
}
export function GenericDeserializeInto<T>(json : any, type : INewable<T>, instance : T) : T {
return <T>DeserializeInto(json, type, instance);
}
//these are used for transforming keys from one format to another
var serializeKeyTransform : (key : string) => string = null;
var deserializeKeyTransform : (key : string) => string = null;
//setter for deserializing key transform
export function DeserializeKeysFrom(transform : (key : string) => string) {
deserializeKeyTransform = transform;
}
//setter for serializing key transform
export function SerializeKeysTo(transform : (key : string) => string) {
serializeKeyTransform = transform;
}
//this is kinda dumb but typescript doesnt treat enums as a type, but sometimes you still
//want them to be serialized / deserialized, this does the trick but must be called after
//the enum is defined.
export function SerializableEnumeration(e : IEnum) : void {
e.Serialize = function (x : any) : any {
return e[x];
};
e.Deserialize = function (x : any) : any {
return e[x];
};
}
//expose the type map
export {TypeMap as __TypeMap}