Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit c47206a

Browse files
committed
Downgrade dict utils to use IEnum<KVP> + allow converting any IEnum<KVP>
1 parent 1287e4c commit c47206a

File tree

5 files changed

+362
-52
lines changed

5 files changed

+362
-52
lines changed

src/ServiceStack.Text/AutoMappingUtils.cs

Lines changed: 197 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public static object ConvertTo(this object from, Type toType, bool skipConverter
119119
return null;
120120

121121
var fromType = from.GetType();
122-
if (fromType == toType)
122+
if (fromType == toType || toType == typeof(object))
123123
return from;
124124

125125
if (ShouldIgnoreMapping(fromType, toType))
@@ -196,6 +196,27 @@ public static object ChangeValueType(object from, Type toType)
196196
return Convert.ChangeType(from, toType, provider: null);
197197
}
198198

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+
199220
return TypeSerializer.DeserializeFromString(from.ToJsv(), toType);
200221
}
201222

@@ -688,48 +709,196 @@ public static IEnumerable<KeyValuePair<PropertyInfo, T>> GetPropertyAttributes<T
688709
}
689710
while ((baseType = baseType.BaseType) != null);
690711
}
691-
712+
692713
public static object TryConvertCollections(Type fromType, Type toType, object fromValue)
693714
{
694715
if (fromValue is IEnumerable values)
695716
{
696717
if (fromValue is IDictionary d)
697718
{
698-
if (toType.GetDictionaryEntryTypes(out var toKeyType, out var toValueType))
719+
var obj = toType.CreateInstance();
720+
switch (obj)
699721
{
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+
}
706759
}
707-
return to;
708-
}
709-
else
710-
{
711-
var from = fromValue.ToObjectDictionary();
712-
var to = from.FromObjectDictionary(toType);
713-
return to;
714760
}
715761
}
716-
else
762+
763+
var genericDef = fromType.GetTypeWithGenericTypeDefinitionOf(typeof(IEnumerable<>));
764+
if (genericDef != null)
717765
{
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)
723769
{
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)
726773
{
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+
}
729803
}
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);
732899
}
900+
var ret = TranslateListWithElements.TryTranslateCollections(to.GetType(), toType, to);
901+
return ret ?? fromValue;
733902
}
734903
}
735904

src/ServiceStack.Text/PlatformExtensions.cs

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Runtime.CompilerServices;
99
using System.Runtime.Serialization;
1010
using System.Threading;
11+
using System.Xml;
1112
using ServiceStack.Text;
1213

1314
namespace ServiceStack
@@ -629,7 +630,7 @@ public void SetValue(object instance, object value)
629630
if (SetValueFn == null)
630631
return;
631632

632-
if (value is IReadOnlyDictionary<string, object> dictionary)
633+
if (value is IEnumerable<KeyValuePair<string, object>> dictionary)
633634
{
634635
value = dictionary.FromObjectDictionary(Type);
635636
}
@@ -717,33 +718,50 @@ public static Dictionary<string, object> ToObjectDictionary(this object obj)
717718
return dict;
718719
}
719720

720-
public static bool GetDictionaryEntryTypes(this Type dictType, out Type keyType, out Type valueType)
721+
public static Type GetKeyValuePairTypeDef(this Type dictType)
721722
{
722-
var genericDef = dictType.GetTypeWithGenericTypeDefinitionOf(typeof(IReadOnlyDictionary<,>));
723+
//matches IDictionary<,>, IReadOnlyDictionary<,>, List<KeyValuePair<string, object>>
724+
var genericDef = dictType.GetTypeWithGenericTypeDefinitionOf(typeof(IEnumerable<>));
725+
if (genericDef == null)
726+
return null;
727+
728+
var genericEnumType = genericDef.GetGenericArguments()[0];
729+
return genericEnumType.GetTypeWithGenericTypeDefinitionOf(typeof(KeyValuePair<,>));
730+
}
731+
732+
public static bool GetKeyValuePairsTypes(this Type dictType, out Type keyType, out Type valueType)
733+
{
734+
//matches IDictionary<,>, IReadOnlyDictionary<,>, List<KeyValuePair<string, object>>
735+
var genericDef = dictType.GetTypeWithGenericTypeDefinitionOf(typeof(IEnumerable<>));
723736
if (genericDef != null)
724737
{
725-
var genericArgs = genericDef.GetGenericArguments();
726-
keyType = genericArgs[0];
727-
valueType = genericArgs[1];
728-
return true;
738+
var genericEnumType = genericDef.GetGenericArguments()[0];
739+
var genericKvps = genericEnumType.GetTypeWithGenericTypeDefinitionOf(typeof(KeyValuePair<,>));
740+
if (genericKvps != null)
741+
{
742+
var genericArgs = genericEnumType.GetGenericArguments();
743+
keyType = genericArgs[0];
744+
valueType = genericArgs[1];
745+
return true;
746+
}
729747
}
730748
keyType = valueType = null;
731749
return false;
732750
}
733751

734-
public static object FromObjectDictionary(this IReadOnlyDictionary<string, object> values, Type type)
752+
public static object FromObjectDictionary(this IEnumerable<KeyValuePair<string, object>> values, Type type)
735753
{
736754
if (values == null)
737755
return null;
738756

739-
var alreadyDict = typeof(IReadOnlyDictionary<string, object>).IsAssignableFrom(type);
757+
var alreadyDict = typeof(IEnumerable<KeyValuePair<string, object>>).IsAssignableFrom(type);
740758
if (alreadyDict)
741759
return values;
742760

743761
var to = type.CreateInstance();
744762
if (to is IDictionary d)
745763
{
746-
if (type.GetDictionaryEntryTypes(out var toKeyType, out var toValueType))
764+
if (type.GetKeyValuePairsTypes(out var toKeyType, out var toValueType))
747765
{
748766
foreach (var entry in values)
749767
{
@@ -768,15 +786,15 @@ public static object FromObjectDictionary(this IReadOnlyDictionary<string, objec
768786
return to;
769787
}
770788

771-
public static void PopulateInstance(this IReadOnlyDictionary<string, object> values, object instance)
789+
public static void PopulateInstance(this IEnumerable<KeyValuePair<string, object>> values, object instance)
772790
{
773791
if (values == null || instance == null)
774792
return;
775793

776794
PopulateInstanceInternal(values, instance, instance.GetType());
777795
}
778796

779-
private static void PopulateInstanceInternal(IReadOnlyDictionary<string, object> values, object to, Type type)
797+
private static void PopulateInstanceInternal(IEnumerable<KeyValuePair<string, object>> values, object to, Type type)
780798
{
781799
if (!toObjectMapCache.TryGetValue(type, out var def))
782800
toObjectMapCache[type] = def = CreateObjectDictionaryDefinition(type);
@@ -793,7 +811,7 @@ private static void PopulateInstanceInternal(IReadOnlyDictionary<string, object>
793811
}
794812
}
795813

796-
public static T FromObjectDictionary<T>(this IReadOnlyDictionary<string, object> values)
814+
public static T FromObjectDictionary<T>(this IEnumerable<KeyValuePair<string, object>> values)
797815
{
798816
return (T)values.FromObjectDictionary(typeof(T));
799817
}
@@ -885,9 +903,9 @@ public static Dictionary<string, object> MergeIntoObjectDictionary(this object o
885903
return to;
886904
}
887905

888-
public static Dictionary<string, string> ToStringDictionary(this IReadOnlyDictionary<string, object> from) => ToStringDictionary(from, null);
906+
public static Dictionary<string, string> ToStringDictionary(this IEnumerable<KeyValuePair<string, object>> from) => ToStringDictionary(from, null);
889907

890-
public static Dictionary<string, string> ToStringDictionary(this IReadOnlyDictionary<string, object> from, IEqualityComparer<string> comparer)
908+
public static Dictionary<string, string> ToStringDictionary(this IEnumerable<KeyValuePair<string, object>> from, IEqualityComparer<string> comparer)
891909
{
892910
var to = comparer != null
893911
? new Dictionary<string, string>(comparer)

src/ServiceStack.Text/TranslateListWithElements.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ public static ICollection<T> TranslateToGenericICollection(
195195
var to = (ICollection<T>)CreateInstance(toInstanceOfType);
196196
foreach (var item in fromList)
197197
{
198-
if (item is IReadOnlyDictionary<string, object> dictionary)
198+
if (item is IEnumerable<KeyValuePair<string, object>> dictionary)
199199
{
200200
var convertedItem = dictionary.FromObjectDictionary<T>();
201201
to.Add(convertedItem);

0 commit comments

Comments
 (0)