1010using System . Runtime . Serialization ;
1111using System . Threading ;
1212using ServiceStack . Text ;
13+ using ServiceStack . Text . Json ;
1314
1415namespace ServiceStack
1516{
@@ -18,8 +19,16 @@ public class CustomHttpResult { }
1819
1920 public static class AutoMappingUtils
2021 {
21- public static T ConvertTo < T > ( this object from )
22+ public static T ConvertTo < T > ( this object from )
2223 {
24+ if ( typeof ( IEnumerable ) . IsAssignableFromType ( typeof ( T ) ) )
25+ {
26+ var listResult = TranslateListWithElements . TryTranslateCollections (
27+ from . GetType ( ) , typeof ( T ) , from ) ;
28+
29+ return ( T ) listResult ;
30+ }
31+
2332 var to = typeof ( T ) . CreateInstance < T > ( ) ;
2433 return to . PopulateWith ( from ) ;
2534 }
@@ -100,7 +109,7 @@ private static object PopulateObjectInternal(object obj, Dictionary<Type, int> r
100109 }
101110 return obj ;
102111 }
103-
112+
104113 private static Dictionary < Type , object > DefaultValueTypes = new Dictionary < Type , object > ( ) ;
105114
106115 public static object GetDefaultValue ( this Type type )
@@ -130,7 +139,7 @@ private static readonly ConcurrentDictionary<string, AssignmentDefinition> Assig
130139
131140 internal static AssignmentDefinition GetAssignmentDefinition ( Type toType , Type fromType )
132141 {
133- var cacheKey = toType . FullName + "<" + fromType . FullName ;
142+ var cacheKey = CreateCacheKey ( fromType , toType ) ;
134143
135144 return AssignmentDefinitionCache . GetOrAdd ( cacheKey , delegate
136145 {
@@ -157,6 +166,12 @@ internal static AssignmentDefinition GetAssignmentDefinition(Type toType, Type f
157166 } ) ;
158167 }
159168
169+ internal static string CreateCacheKey ( Type fromType , Type toType )
170+ {
171+ var cacheKey = fromType . FullName + ">" + toType . FullName ;
172+ return cacheKey ;
173+ }
174+
160175 private static Dictionary < string , AssignmentMember > GetMembers ( Type type , bool isReadable )
161176 {
162177 var map = new Dictionary < string , AssignmentMember > ( ) ;
@@ -585,82 +600,45 @@ public void Populate(object to, object from,
585600 Func < PropertyInfo , bool > propertyInfoPredicate ,
586601 Func < object , Type , bool > valuePredicate )
587602 {
588- foreach ( var assignmentEntry in AssignmentMemberMap )
603+ foreach ( var assignmentEntryMap in AssignmentMemberMap )
589604 {
590- var assignmentMember = assignmentEntry . Value ;
591- var fromMember = assignmentEntry . Value . From ;
592- var toMember = assignmentEntry . Value . To ;
605+ var assignmentEntry = assignmentEntryMap . Value ;
606+ var fromMember = assignmentEntry . From ;
607+ var toMember = assignmentEntry . To ;
593608
594609 if ( fromMember . PropertyInfo != null && propertyInfoPredicate != null )
595610 {
596611 if ( ! propertyInfoPredicate ( fromMember . PropertyInfo ) ) continue ;
597612 }
598613
614+ var fromType = fromMember . Type ;
615+ var toType = toMember . Type ;
599616 try
600617 {
601- var fromValue = assignmentMember . GetValueFn ( from ) ;
618+ var fromValue = assignmentEntry . GetValueFn ( from ) ;
602619
603620 if ( valuePredicate != null )
604621 {
605622 if ( ! valuePredicate ( fromValue , fromMember . PropertyInfo . PropertyType ) ) continue ;
606623 }
607624
608- if ( fromMember . Type != toMember . Type )
625+ if ( fromType != toType )
609626 {
610- if ( fromMember . Type == typeof ( string ) )
611- {
612- fromValue = TypeSerializer . DeserializeFromString ( ( string ) fromValue , toMember . Type ) ;
613- }
614- else if ( toMember . Type == typeof ( string ) )
615- {
616- fromValue = TypeSerializer . SerializeToString ( fromValue ) ;
617- }
618- else if ( toMember . Type . IsEnum ( ) || fromMember . Type . IsEnum ( ) )
619- {
620- if ( toMember . Type . IsEnum ( ) && fromMember . Type . IsEnum ( ) )
621- {
622- fromValue = Enum . Parse ( toMember . Type , fromValue . ToString ( ) ) ;
623- }
624- else if ( toMember . Type . IsNullableType ( ) )
625- {
626- var genericArg = toMember . Type . GenericTypeArguments ( ) [ 0 ] ;
627- if ( genericArg . IsEnum ( ) )
628- {
629- fromValue = Enum . ToObject ( genericArg , fromValue ) ;
630- }
631- }
632- else if ( toMember . Type . IsIntegerType ( ) )
633- {
634- fromValue = Enum . ToObject ( fromMember . Type , fromValue ) ;
635- }
636- }
637- else if ( typeof ( IEnumerable ) . IsAssignableFrom ( fromMember . Type ) )
638- {
639- var listResult = TranslateListWithElements . TryTranslateToGenericICollection (
640- fromMember . Type , toMember . Type , fromValue ) ;
641-
642- if ( listResult != null )
643- {
644- fromValue = listResult ;
645- }
646- }
647- else if ( ! ( toMember . Type . IsValueType ( )
648- || toMember . Type . IsNullableType ( ) ) )
627+ var converterFn = TypeConverter . GetTypeConverter ( fromType , toType ) ;
628+ if ( converterFn != null )
649629 {
650- var toValue = toMember . Type . CreateInstance ( ) ;
651- toValue . PopulateWith ( fromValue ) ;
652- fromValue = toValue ;
630+ fromValue = converterFn ( fromValue ) ;
653631 }
654632 }
655633
656- var setterFn = assignmentMember . SetValueFn ;
634+ var setterFn = assignmentEntry . SetValueFn ;
657635 setterFn ( to , fromValue ) ;
658636 }
659637 catch ( Exception ex )
660638 {
661639 Tracer . Instance . WriteWarning ( "Error trying to set properties {0}.{1} > {2}.{3}:\n {4}" ,
662- FromType . FullName , fromMember . Type . Name ,
663- ToType . FullName , toMember . Type . Name , ex ) ;
640+ FromType . FullName , fromType . Name ,
641+ ToType . FullName , toType . Name , ex ) ;
664642 }
665643 }
666644 }
@@ -694,4 +672,95 @@ public static PropertyGetterDelegate GetFieldGetterFn(this FieldInfo fieldInfo)
694672 return PclExport . Instance . GetFieldGetterFn ( fieldInfo ) ;
695673 }
696674 }
675+
676+ internal static class TypeConverter
677+ {
678+ internal static ConcurrentDictionary < string , PropertyGetterDelegate > TypeConvertersCache
679+ = new ConcurrentDictionary < string , PropertyGetterDelegate > ( ) ;
680+
681+ public static PropertyGetterDelegate GetTypeConverter ( Type fromType , Type toType )
682+ {
683+ var cacheKey = AutoMappingUtils . CreateCacheKey ( fromType , toType ) ;
684+
685+ return TypeConvertersCache . GetOrAdd ( cacheKey ,
686+ ( Func < string , PropertyGetterDelegate > ) ( key => CreateTypeConverter ( fromType , toType ) ) ) ;
687+ }
688+
689+ public static PropertyGetterDelegate CreateTypeConverter ( Type fromType , Type toType )
690+ {
691+ if ( fromType == toType )
692+ return null ;
693+
694+ if ( fromType == typeof ( string ) )
695+ {
696+ return fromValue => TypeSerializer . DeserializeFromString ( ( string ) fromValue , toType ) ;
697+ }
698+ if ( toType == typeof ( string ) )
699+ {
700+ return TypeSerializer . SerializeToString ;
701+ }
702+ if ( toType . IsEnum ( ) || fromType . IsEnum ( ) )
703+ {
704+ if ( toType . IsEnum ( ) && fromType . IsEnum ( ) )
705+ {
706+ return fromValue => Enum . Parse ( toType , fromValue . ToString ( ) ) ;
707+ }
708+ if ( toType . IsNullableType ( ) )
709+ {
710+ var genericArg = toType . GenericTypeArguments ( ) [ 0 ] ;
711+ if ( genericArg . IsEnum ( ) )
712+ {
713+ return fromValue => Enum . ToObject ( genericArg , fromValue ) ;
714+ }
715+ }
716+ else if ( toType . IsIntegerType ( ) )
717+ {
718+ return fromValue => Enum . ToObject ( fromType , fromValue ) ;
719+ }
720+ }
721+ else if ( toType . IsNullableType ( ) )
722+ {
723+ return null ;
724+ }
725+ else if ( typeof ( IEnumerable ) . IsAssignableFrom ( fromType ) )
726+ {
727+ return fromValue =>
728+ {
729+ var listResult = TranslateListWithElements . TryTranslateCollections (
730+ fromType , toType , fromValue ) ;
731+
732+ return listResult ?? fromValue ;
733+ } ;
734+ }
735+ else if ( toType . IsValueType ( ) )
736+ {
737+ return fromValue => Convert . ChangeType ( fromValue , toType ) ;
738+ }
739+ else
740+ {
741+ return fromValue =>
742+ {
743+ var toValue = toType . CreateInstance ( ) ;
744+ toValue . PopulateWith ( fromValue ) ;
745+ return toValue ;
746+ } ;
747+ }
748+
749+ return null ;
750+ }
751+ }
752+
753+ internal class TypeConverter < From , To >
754+ {
755+ static TypeConverter ( )
756+ {
757+ ConvertFn = TypeConverter . CreateTypeConverter ( typeof ( From ) , typeof ( To ) ) ;
758+ }
759+
760+ public static PropertyGetterDelegate ConvertFn ;
761+ public PropertyGetterDelegate GetConvertFn ( )
762+ {
763+ return ConvertFn ;
764+ }
765+ }
697766}
0 commit comments