Skip to content

Instantly share code, notes, and snippets.

@xperiments
Created June 24, 2014 17:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xperiments/d5420a55a1eb7ed407c1 to your computer and use it in GitHub Desktop.
Save xperiments/d5420a55a1eb7ed407c1 to your computer and use it in GitHub Desktop.
module io.xperiments.utils.serialize
{
/**
* The mini
*/
export interface ISerializableObject
{
"@serializable":string;
}
export interface ISerializable extends ISerializableObject
{
"@serializable":string;
writeObject( root:boolean ):any;
readObject( obj:any ):void;
}
export class Serializable implements ISerializable
{
public "@serializable":string;
/**
*
* @returns {any}
*/
public writeObject():any
{
return Serializer.writeObject( this );
}
/**
*
* @param obj
*/
public readObject(obj:any):void
{
Serializer.readObject(this, obj);
}
}
export interface ISerializableRegisters
{
[key:string]:ISerializableRegister;
}
export interface ISerializableRegister
{
keys:string[];
serializerData:any;
}
export class Serializer
{
private static serializableRegisters:ISerializableRegisters = {};
/**
*
* @param classContext
* @param SerializerDataClass
*/
public static registerClass( classContext:()=>any, SerializerDataClass:any ):void
{
// determine class global path by parsing the body of the classContext Function
var classPath:string = /return ([A-Za-z0-9_$]*)/g.exec(classContext.toString())[1];
// Check if class has been processed
if( Serializer.serializableRegisters[ classPath ] )
{
throw new Error('Class '+classPath+' already registered');
}
Serializer.getClassFromPath( classPath ).prototype['@serializable'] = classPath;
Serializer.serializableRegisters[classPath] =
{
keys:Serializer.getMixedNames( SerializerDataClass ),
serializerData:SerializerDataClass
};
}
/**
*
* @param instance
* @returns {any}
*/
public static writeObject( instance:ISerializable ):any
{
var obj:any = {};
var register:ISerializableRegister = Serializer.getSerializableRegister( instance );
register.keys.filter((key)=>{ return key.indexOf('set_')!=0 && key.indexOf('get_')!=0 }).forEach(( key:string )=>
{
var value:any = instance[key];
if( !value ) return; // don't getSerializableProperties void/empty/undefined
Serializer.writeAny( value, key, obj, register.serializerData );
});
return obj;
}
/**
*
* @param instance
* @param obj
*/
public static readObject( instance:ISerializable, obj:any ):void
{
var register:ISerializableRegister = Serializer.getSerializableRegister( instance );
Serializer.getSerializableRegister( instance ).keys
.forEach( ( key:string )=> Serializer.readAny( obj[key], key, instance, register.serializerData ) );
}
// Private Methods
/**
*
* @param array
* @returns {any[]}
*/
private static writeArray( array:any[] ):any[]
{
var returnArray:any[] = [];
array.forEach( ( element , i )=> Serializer.writeAny( element,i,returnArray , Serializer.getSerializableRegister( element[i] ).serializerData) );
return returnArray;
}
/**
*
* @param element
* @param key
* @param target
* @param SerializerDataClass
*/
private static writeAny( element:any,key:any,target:any, SerializerDataClass:any )
{
if( typeof SerializerDataClass.prototype["set_"+key] == "function" )
{
target[key] = SerializerDataClass.prototype["set_"+key]( element );
return;
}
var elementType = typeof element;
switch( true )
{
case elementType=="boolean":
case elementType=="string":
case elementType=="number":
target[key] = element;
break;
case Array.isArray( element ):
target[key] = Serializer.writeArray( element );
break;
case elementType=="object" && !Array.isArray( element ):
console.log( element instanceof Date,'0000')
target[key] = Serializer.isExternalizable( element ) ? Serializer.writeObject( element ):JSON.parse(JSON.stringify( element ));
break;
}
}
/**
*
* @param array
* @returns {any[]}
*/
private static readArray( array:any[] ):any[]
{
var resultArray:any[] = [];
array.forEach( ( element, i )=>Serializer.readAny( element, i, resultArray, Serializer.getSerializableRegister( element[i] ).serializerData));
return resultArray;
}
/**
*
* @param element
* @param key
* @param target
* @param SerializerDataClass
*/
private static readAny( element:any, key:any, target:any, SerializerDataClass:any )
{
if( typeof SerializerDataClass.prototype["get_"+key] == "function" )
{
target[key] = SerializerDataClass.prototype["get_"+key]( element );
return;
}
var type:string = typeof element;
switch( true )
{
case type=="boolean":
case type=="string":
case type=="number":
target[key] = element;
break;
case Array.isArray( element ):
target[key] = Serializer.readArray( element );
break;
case type=="object" && !Array.isArray( element ):
if( element.hasOwnProperty('@serializable') )
{
var moduleParts:string[] = element['@serializable'].split('.');
var classPath:string = moduleParts.join('.');
if( !target[key] ) target[key] = Serializer.getClass(classPath);
target[key].readObject( element );
}
else
{
target[key] = element;
}
break;
}
}
/* Helper Methods */
/**
*
* @param SerializerDataClass
* @returns {string[]}
*/
private static getMixedNames( SerializerDataClass:any ):string[]
{
return Object.getOwnPropertyNames( new SerializerDataClass() )//.concat( Object.keys( SerializerDataClass.prototype ));
}
/**
*
* @param instance
* @returns {boolean}
*/
private static isExternalizable( instance ):boolean
{
return '@serializable' in instance && typeof instance.writeObject == "function" && typeof instance.readObject == "function";
}
/**
*
* @param name
* @param context
* @returns {any}
*/
private static getClassFromPath( name:string , context:any = window ):any
{
name.split('.').forEach( ctx=>context = context[ ctx ] );
return context;
}
/**
*
* @param name
* @param context
* @returns {any}
*/
private static getClass( name:string , context:any = window ):any
{
name.split('.').forEach( ctx=>context = context[ ctx ] );
return new context;
}
/**
*
* @param instance
* @returns {ISerializableRegister}
*/
private static getSerializableRegister( instance:ISerializable ):ISerializableRegister
{
var props:ISerializableRegister = Serializer.serializableRegisters[ instance['@serializable'] ];
if(!props)
{
throw new Error('Unable to get serializable properties for class '+instance['@serializable'] )
}
return props;
}
}
}
/* Simple TEST */
import ISerializableObject = io.xperiments.utils.serialize.ISerializableObject;
import Serializer = io.xperiments.utils.serialize.Serializer;
import Serializable = io.xperiments.utils.serialize.Serializable;
// the actual real class
class Data extends Serializable
{
"@serializable":string;
name:string;
address:string;
date:Date;
constructor()
{
super();
this.name = "foo";
this.address = "address";
this.date = new Date();
this.date.setMonth(1)
}
}
// here our values pattern to export
class DataSerializer implements ISerializableObject
{
"@serializable":string = null;
name:string = null;
date:string = null;
set_date(date:Date):string{return [ date.getFullYear(), date.getMonth()+1, date.getDate()].join('/')}
get_date(dateString:string):Date
{
var dateParts:string[] = dateString.split('/');
var date = new Date();
date.setFullYear( parseInt(dateParts[0],10));
date.setMonth( parseInt(dateParts[1],10)-1);
date.setDate( parseInt(dateParts[2],10));
return date;
}
}
Serializer.registerClass( ()=>Data, DataSerializer );
class ISubData implements ISerializableObject
{
"@serializable":string = null;
data:Data = null;
name:string = null;
date:string = null;
image:HTMLImageElement = null;
set_image( image:HTMLImageElement ):string
{
var canvas:HTMLCanvasElement = document.createElement('canvas');
canvas.width = image.width;
canvas.height = image.height;
canvas.getContext('2d').drawImage( image,0,0 );
return canvas.toDataURL();
}
get_image( image64:string ):HTMLImageElement
{
var img:HTMLImageElement = document.createElement('img');
img.src = image64;
return img;
}
set_date(date:Date):string{console.log('paso write');return [ date.getFullYear(), date.getMonth()+1, date.getDate()].join('/')}
get_date(dateString:string):Date
{
console.log('paso read');
var dateParts:string[] = dateString.split('/');
var date = new Date();
date.setFullYear( parseInt(dateParts[0],10));
date.setMonth( parseInt(dateParts[1],10)-1);
date.setDate( parseInt(dateParts[2],10));
return date;
}
}
// the actual real class
class SubData extends Serializable implements ISerializableObject
{
"@serializable":string;
data:Data;
name:string = "PEDRO";
date:Date = new Date();
image:HTMLImageElement;
constructor()
{
super();
this.data = new Data();
this.data.name = "SOME OTHER VALUE";
this.name = "JUAN";
this.data.address="SAMANIEGO 67";
this.image = document.createElement('img');
}
}
Serializer.registerClass( ()=>SubData, ISubData );
var c = new SubData();
c.name="NEW VALUE";
/* ATTENTION CHANGE IMAGE HERE */
c.image.src = "iphone-6-concept-Copiar.jpg";
setTimeout(()=>{
var d = new SubData();
d.readObject( c.writeObject() );
document.body.appendChild( d.image )
},5000);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment