import 'reflect-metadata' ; import { isTargetType , isPrimitiveOrPrimitiveClass , isArrayOrArrayClass } from './libs/utils' ; interface export interface IGenericObject { [ key ] : any ; } const const JSON_META_DATA_KEY = 'JsonProperty' ; interface export interface ICustomConverter { fromJson ( data ) : any ; toJson ( data ) : any ; } interface property property export interface IDecoratorMetaData < T > { name ? : string ; clazz ? : { new ( ) : T ; } ; customConverter ? : ICustomConverter ; excludeToJson ? : boolean ; } class property property class DecoratorMetaData < T > { constructor ( public name ? , public clazz ? ) { } } function property return { ( target : Object , targetKey : string | symbol ) => void } export function JsonProperty < T > ( metadata ? ) { let decoratorMetaData ; if ( isTargetType ( metadata , 'string' ) ) { decoratorMetaData = new DecoratorMetaData < T > ( metadata as string ) ; } else if ( isTargetType ( metadata , 'object' ) ) { decoratorMetaData = metadata as IDecoratorMetaData < T > ; } else { throw new Error ( 'index.ts: meta data in Json property is undefined. meta data: ' + metadata ) ; } return Reflect . metadata ( JSON_META_DATA_KEY , decoratorMetaData ) ; } function property property return { Function } description function getClazz < T > ( target , propertyKey ) { return Reflect . getMetadata ( 'design:type' , target , propertyKey ) ; } function property property return { IDecoratorMetaData < T > } function getJsonProperty < T > ( target , propertyKey ) { return Reflect . getMetadata ( JSON_META_DATA_KEY , target , propertyKey ) ; } function property return { IDecoratorMetaData < T > } function hasAnyNullOrUndefined ( ... args ) { return args . some ( ( arg ) => arg === null || arg === undefined ) ; } function mapFromJson < T > ( decoratorMetadata , instance , json , key ) { /**
     * if decorator name is not found, use target property key as decorator name. It means mapping it directly
     */ let decoratorName = decoratorMetadata . name || key ; let innerJson = json ? json [ decoratorName ] : undefined ; let clazz = getClazz ( instance , key ) ; if ( isArrayOrArrayClass ( clazz ) ) { let metadata = getJsonProperty ( instance , key ) ; if ( metadata && metadata . clazz || isPrimitiveOrPrimitiveClass ( clazz ) ) { if ( innerJson && isArrayOrArrayClass ( innerJson ) ) { return innerJson . map ( ( item ) => deserialize ( metadata . clazz , item ) ) ; } return ; } else { return innerJson ; } } if ( ! isPrimitiveOrPrimitiveClass ( clazz ) ) { return deserialize ( clazz , innerJson ) ; } return json ? json [ decoratorName ] : undefined ; } function param { { new ( ) : T } } clazz param { Object } json return { T } export function deserialize < T extends IGenericObject > ( Clazz , json ) { /**
     * As it is a recursive function, ignore any arguments that are unset
     */ if ( hasAnyNullOrUndefined ( Clazz , json ) ) { return void 0 ; } /**
     * Prevent non-json continue
     */ if ( ! isTargetType ( json , 'object' ) ) { return void 0 ; } /**
     * init root class to contain json
     */ let instance = new Clazz ( ) ; Object . keys ( instance ) . forEach ( ( key ) => { /**
         * get decoratorMetaData, structure: { name?:string, clazz?:{ new():T } }
         */ let decoratorMetaData = getJsonProperty ( instance , key ) ; /**
         * pass value to instance
         */ if ( decoratorMetaData && decoratorMetaData . customConverter ) { instance [ key ] = decoratorMetaData . customConverter . fromJson ( json [ decoratorMetaData . name || key ] ) ; } else { instance [ key ] = decoratorMetaData ? mapFromJson ( decoratorMetaData , instance , json , key ) : json [ key ] ; } } ) ; return instance as T ; } param instance returns { any } export function serialize ( instance ) { if ( ! isTargetType ( instance , 'object' ) || isArrayOrArrayClass ( instance ) ) { return instance ; } const obj = { } ; Object . keys ( instance ) . forEach ( key => { const metadata = getJsonProperty ( instance , key ) ; obj [ metadata && metadata . name ? metadata . name : key ] = serializeProperty ( metadata , instance [ key ] ) ; } ) ; return obj ; } param metadata param prop returns { any } function serializeProperty ( metadata , prop ) { if ( ! metadata || metadata . excludeToJson === true ) { return ; } if ( metadata . customConverter ) { return metadata . customConverter . toJson ( prop ) ; } if ( ! metadata . clazz ) { return prop ; } if ( isArrayOrArrayClass ( prop ) ) { return prop . map ( ( propItem ) => serialize ( propItem ) ) ; } return serialize ( prop ) ; }