@@ -119,7 +119,7 @@ public static object ConvertTo(this object from, Type toType, bool skipConverter
119
119
return null ;
120
120
121
121
var fromType = from . GetType ( ) ;
122
- if ( fromType == toType )
122
+ if ( fromType == toType || toType == typeof ( object ) )
123
123
return from ;
124
124
125
125
if ( ShouldIgnoreMapping ( fromType , toType ) )
@@ -196,6 +196,27 @@ public static object ChangeValueType(object from, Type toType)
196
196
return Convert . ChangeType ( from , toType , provider : null ) ;
197
197
}
198
198
199
+ var toKvpType = toType . GetTypeWithGenericTypeDefinitionOf ( typeof ( KeyValuePair < , > ) ) ;
200
+ if ( toKvpType != null )
201
+ {
202
+ var fromKvpType = fromType . GetTypeWithGenericTypeDefinitionOf ( typeof ( KeyValuePair < , > ) ) ;
203
+ if ( fromKvpType != null )
204
+ {
205
+ var fromProps = TypeProperties . Get ( fromKvpType ) ;
206
+ var fromKey = fromProps . GetPublicGetter ( "Key" ) ( from ) ;
207
+ var fromValue = fromProps . GetPublicGetter ( "Value" ) ( from ) ;
208
+
209
+ var toKvpArgs = toKvpType . GetGenericArguments ( ) ;
210
+ var toKeyType = toKvpArgs [ 0 ] ;
211
+ var toValueType = toKvpArgs [ 1 ] ;
212
+ var toCtor = toKvpType . GetConstructor ( toKvpArgs ) ;
213
+ var toKey = fromKey . ConvertTo ( toKeyType ) ;
214
+ var toValue = fromValue . ConvertTo ( toValueType ) ;
215
+ var to = toCtor . Invoke ( new [ ] { toKey , toValue } ) ;
216
+ return to ;
217
+ }
218
+ }
219
+
199
220
return TypeSerializer . DeserializeFromString ( from . ToJsv ( ) , toType ) ;
200
221
}
201
222
@@ -688,48 +709,196 @@ public static IEnumerable<KeyValuePair<PropertyInfo, T>> GetPropertyAttributes<T
688
709
}
689
710
while ( ( baseType = baseType . BaseType ) != null ) ;
690
711
}
691
-
712
+
692
713
public static object TryConvertCollections ( Type fromType , Type toType , object fromValue )
693
714
{
694
715
if ( fromValue is IEnumerable values )
695
716
{
696
717
if ( fromValue is IDictionary d )
697
718
{
698
- if ( toType . GetDictionaryEntryTypes ( out var toKeyType , out var toValueType ) )
719
+ var obj = toType . CreateInstance ( ) ;
720
+ switch ( obj )
699
721
{
700
- var to = ( IDictionary ) toType . CreateInstance ( ) ;
701
- foreach ( var key in d . Keys )
702
- {
703
- var toKey = key . ConvertTo ( toKeyType ) ;
704
- var toValue = d [ key ] . ConvertTo ( toValueType ) ;
705
- to [ toKey ] = toValue ;
722
+ case List < KeyValuePair < string , string > > toList : {
723
+ foreach ( var key in d . Keys )
724
+ {
725
+ var toKey = key . ConvertTo < string > ( ) ;
726
+ var toValue = d [ key ] . ConvertTo < string > ( ) ;
727
+ toList . Add ( new KeyValuePair < string , string > ( toKey , toValue ) ) ;
728
+ }
729
+ return toList ;
730
+ }
731
+ case List < KeyValuePair < string , object > > toObjList : {
732
+ foreach ( var key in d . Keys )
733
+ {
734
+ var toKey = key . ConvertTo < string > ( ) ;
735
+ var toValue = d [ key ] ;
736
+ toObjList . Add ( new KeyValuePair < string , object > ( toKey , toValue ) ) ;
737
+ }
738
+ return toObjList ;
739
+ }
740
+ case IDictionary toDict : {
741
+ if ( toType . GetKeyValuePairsTypes ( out var toKeyType , out var toValueType ) )
742
+ {
743
+ foreach ( var key in d . Keys )
744
+ {
745
+ var toKey = toKeyType != null
746
+ ? key . ConvertTo ( toKeyType )
747
+ : key ;
748
+ var toValue = d [ key ] . ConvertTo ( toValueType ) ;
749
+ toDict [ toKey ] = toValue ;
750
+ }
751
+ return toDict ;
752
+ }
753
+ else
754
+ {
755
+ var from = fromValue . ToObjectDictionary ( ) ;
756
+ var to = from . FromObjectDictionary ( toType ) ;
757
+ return to ;
758
+ }
706
759
}
707
- return to ;
708
- }
709
- else
710
- {
711
- var from = fromValue . ToObjectDictionary ( ) ;
712
- var to = from . FromObjectDictionary ( toType ) ;
713
- return to ;
714
760
}
715
761
}
716
- else
762
+
763
+ var genericDef = fromType . GetTypeWithGenericTypeDefinitionOf ( typeof ( IEnumerable < > ) ) ;
764
+ if ( genericDef != null )
717
765
{
718
- var fromElementType = fromType . GetCollectionType ( ) ;
719
- var toElementType = toType . GetCollectionType ( ) ;
720
-
721
- if ( fromElementType != null && toElementType != null && fromElementType != toElementType &&
722
- ! ( typeof ( IDictionary ) . IsAssignableFrom ( fromElementType ) || typeof ( IDictionary ) . IsAssignableFrom ( toElementType ) ) )
766
+ var genericEnumType = genericDef . GetGenericArguments ( ) [ 0 ] ;
767
+ var genericKvps = genericEnumType . GetTypeWithGenericTypeDefinitionOf ( typeof ( KeyValuePair < , > ) ) ;
768
+ if ( genericKvps != null )
723
769
{
724
- var to = new List < object > ( ) ;
725
- foreach ( var item in values )
770
+ // Improve perf with Specialized handling of common KVP combinations
771
+ var obj = toType . CreateInstance ( ) ;
772
+ if ( fromValue is IEnumerable < KeyValuePair < string , string > > sKvps )
726
773
{
727
- var toItem = item . ConvertTo ( toElementType ) ;
728
- to . Add ( toItem ) ;
774
+ switch ( obj ) {
775
+ case IDictionary toDict : {
776
+ toType . GetKeyValuePairsTypes ( out var toKeyType , out var toValueType ) ;
777
+ foreach ( var entry in sKvps )
778
+ {
779
+ var toKey = toKeyType != null
780
+ ? entry . Key . ConvertTo ( toKeyType )
781
+ : entry . Key ;
782
+ toDict [ toKey ] = toValueType != null
783
+ ? entry . Value . ConvertTo ( toValueType )
784
+ : entry . Value ;
785
+ }
786
+ return toDict ;
787
+ }
788
+ case List < KeyValuePair < string , string > > toList : {
789
+ foreach ( var entry in sKvps )
790
+ {
791
+ toList . Add ( new KeyValuePair < string , string > ( entry . Key , entry . Value ) ) ;
792
+ }
793
+ return toList ;
794
+ }
795
+ case List < KeyValuePair < string , object > > toObjList : {
796
+ foreach ( var entry in sKvps )
797
+ {
798
+ toObjList . Add ( new KeyValuePair < string , object > ( entry . Key , entry . Value ) ) ;
799
+ }
800
+ return toObjList ;
801
+ }
802
+ }
729
803
}
730
- var ret = TranslateListWithElements . TryTranslateCollections ( to . GetType ( ) , toType , to ) ;
731
- return ret ?? fromValue ;
804
+ else if ( fromValue is IEnumerable < KeyValuePair < string , object > > oKvps )
805
+ {
806
+ switch ( obj ) {
807
+ case IDictionary toDict :
808
+ {
809
+ toType . GetKeyValuePairsTypes ( out var toKeyType , out var toValueType ) ;
810
+ foreach ( var entry in oKvps )
811
+ {
812
+ var toKey = entry . Key . ConvertTo < string > ( ) ;
813
+ toDict [ toKey ] = toValueType != null
814
+ ? entry . Value . ConvertTo ( toValueType )
815
+ : entry . Value ;
816
+ }
817
+ return toDict ;
818
+ }
819
+ case List < KeyValuePair < string , string > > toList : {
820
+ foreach ( var entry in oKvps )
821
+ {
822
+ toList . Add ( new KeyValuePair < string , string > ( entry . Key , entry . Value . ConvertTo < string > ( ) ) ) ;
823
+ }
824
+ return toList ;
825
+ }
826
+ case List < KeyValuePair < string , object > > toObjList : {
827
+ foreach ( var entry in oKvps )
828
+ {
829
+ toObjList . Add ( new KeyValuePair < string , object > ( entry . Key , entry . Value ) ) ;
830
+ }
831
+ return toObjList ;
832
+ }
833
+ }
834
+ }
835
+
836
+
837
+ // Fallback for handling any KVP combo
838
+ var toKvpDefType = toType . GetKeyValuePairTypeDef ( ) ;
839
+ switch ( obj ) {
840
+ case IDictionary toDict :
841
+ {
842
+ var keyProp = TypeProperties . Get ( toKvpDefType ) . GetPublicGetter ( "Key" ) ;
843
+ var valueProp = TypeProperties . Get ( toKvpDefType ) . GetPublicGetter ( "Value" ) ;
844
+
845
+ foreach ( var entry in values )
846
+ {
847
+ var toKvp = entry . ConvertTo ( toKvpDefType ) ;
848
+ var toKey = keyProp ( toKvp ) ;
849
+ var toValue = valueProp ( toKvp ) ;
850
+ toDict [ toKey ] = toValue ;
851
+ }
852
+ return toDict ;
853
+ }
854
+ case List < KeyValuePair < string , string > > toStringList : {
855
+ foreach ( var entry in values )
856
+ {
857
+ var toEntry = entry . ConvertTo ( toKvpDefType ) ;
858
+ toStringList . Add ( ( KeyValuePair < string , string > ) toEntry ) ;
859
+ }
860
+ return toStringList ;
861
+ }
862
+ case List < KeyValuePair < string , object > > toObjList : {
863
+ foreach ( var entry in values )
864
+ {
865
+ var toEntry = entry . ConvertTo ( toKvpDefType ) ;
866
+ toObjList . Add ( ( KeyValuePair < string , object > ) toEntry ) ;
867
+ }
868
+ return toObjList ;
869
+ }
870
+ case IEnumerable toList :
871
+ {
872
+ var addMethod = toType . GetMethod ( nameof ( IList . Add ) , new [ ] { toKvpDefType } ) ;
873
+ if ( addMethod != null )
874
+ {
875
+ foreach ( var entry in values )
876
+ {
877
+ var toEntry = entry . ConvertTo ( toKvpDefType ) ;
878
+ addMethod . Invoke ( toList , new [ ] { toEntry } ) ;
879
+ }
880
+ return toList ;
881
+ }
882
+ break ;
883
+ }
884
+ }
885
+ }
886
+ }
887
+
888
+ var fromElementType = fromType . GetCollectionType ( ) ;
889
+ var toElementType = toType . GetCollectionType ( ) ;
890
+
891
+ if ( fromElementType != null && toElementType != null && fromElementType != toElementType &&
892
+ ! ( typeof ( IDictionary ) . IsAssignableFrom ( fromElementType ) || typeof ( IDictionary ) . IsAssignableFrom ( toElementType ) ) )
893
+ {
894
+ var to = new List < object > ( ) ;
895
+ foreach ( var item in values )
896
+ {
897
+ var toItem = item . ConvertTo ( toElementType ) ;
898
+ to . Add ( toItem ) ;
732
899
}
900
+ var ret = TranslateListWithElements . TryTranslateCollections ( to . GetType ( ) , toType , to ) ;
901
+ return ret ?? fromValue ;
733
902
}
734
903
}
735
904
0 commit comments