Last active
August 16, 2019 15:47
-
-
Save ashworth-zach/200730666d23a2a2c782af1cb5675fd8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Expando | |
{ | |
public static class ExpandoHelper | |
{ | |
public static dynamic ParseDictionary(IDictionary<string, object> dict, Type explicitType) | |
{ | |
if (dict == null) | |
{ | |
throw new ArgumentNullException("dict", "Dictionary was null, cannot parse a null dictionary"); | |
} | |
object target; | |
if (explicitType.IsArray) | |
{ | |
var length = dict.Keys.Count(); | |
target = (Array)Activator.CreateInstance(explicitType, new object[] { length }); | |
} | |
else | |
{ | |
target = Activator.CreateInstance(explicitType); | |
} | |
foreach (var property in target.GetType().GetProperties()) | |
{ | |
var propertyName = property.Name; | |
object val; | |
if (dict.TryGetValue(propertyName, out val) && val != null) | |
{ | |
var propertyVal = explicitType.GetProperty(propertyName); | |
var expectedType = property.PropertyType; | |
var valType = val.GetType(); | |
if (valType == expectedType) | |
{ | |
propertyVal.SetValue(target, val); | |
} | |
else if (val is IConvertible) | |
{ | |
Type safeType = Nullable.GetUnderlyingType(expectedType) ?? expectedType; | |
if (val == null) | |
{ | |
propertyVal.SetValue(target, null, null); | |
} | |
else | |
{ | |
propertyVal.SetValue(target, Convert.ChangeType(val, safeType), null); | |
} | |
} | |
else if (val is IDictionary<string, object>) | |
{ | |
//Parse non-simple object | |
var propType = propertyVal.PropertyType; | |
object explicitVal = ParseDictionary(val as IDictionary<string, object>, propType); | |
propertyVal.SetValue(target, explicitVal); | |
} | |
else if (val is IList) | |
{ | |
//Parse list/enumeration/array | |
if (!(expectedType.IsArray || expectedType.IsGenericType)) | |
{ | |
//Not sure how we'd get here if we're neither an array nor generic, but we can't really do much | |
continue; | |
} | |
//Create the necessary List implementation that we need | |
var explicitList = ParseAsList(val, expectedType, property); | |
if (expectedType.IsArray) | |
{ | |
//Convert from list to array if necessary | |
var arrayType = expectedType.GetElementType().MakeArrayType(); | |
var array = (Array)Activator.CreateInstance(arrayType, new object[] { explicitList.Count }); | |
explicitList.CopyTo(array, 0); | |
propertyVal.SetValue(target, array); | |
} | |
else | |
{ | |
propertyVal.SetValue(target, explicitList); | |
} | |
} | |
else | |
{ | |
//Attempt to set it - will error if not compatible and all other checks are bypassed | |
propertyVal.SetValue(target, val); | |
} | |
} | |
} | |
return target; | |
} | |
private static IList ParseAsList(object val, Type expectedType, PropertyInfo property) | |
{ | |
Type elementType = null; | |
if (expectedType.IsArray) //Array type is explicitly included with GetElementType | |
{ | |
elementType = expectedType.GetElementType(); | |
} | |
else if (expectedType.IsGenericType) //Get List type by inspecting generic argument | |
{ | |
elementType = expectedType.GetGenericArguments()[0]; | |
} | |
var listType = typeof(List<>).MakeGenericType(elementType); | |
var explicitList = (IList)Activator.CreateInstance(listType); | |
foreach (var element in val as IList<object>) | |
{ | |
object explicitElement = ParseDictionary(element as IDictionary<string, object>, elementType); | |
explicitList.Add(explicitElement); | |
} | |
return explicitList; | |
} | |
public static Tuple<bool,dynamic> TryGetValue(string path, List<Tuple<string,ExpandoObject>> dynamicList) | |
{ | |
string[] pathArr = path.Split("."); | |
int pathIdx = 0; | |
for(var i = 0; i< dynamicList.Count(); i++) | |
{ | |
if (dynamicList[i].Item1.ToLower() == pathArr[pathIdx].ToLower()) | |
{ | |
pathArr = pathArr.Skip(1).ToArray(); | |
ExpandoObject objectToSearch = dynamicList[i].Item2; | |
return ParseExpandoForValue(objectToSearch, pathArr); | |
} | |
} | |
return new Tuple<bool, dynamic>(false, null); | |
} | |
private static Tuple<bool, dynamic> ParseExpandoForValue(ExpandoObject expando, string[] path) | |
{ | |
int pathIdx = 0; | |
//build list | |
List<Tuple<string, dynamic>> expandoList = BuildExpandoList(expando); | |
for (var i = 0; i < expandoList.Count(); i++) | |
{ | |
//This is the item we want | |
if (expandoList[i].Item1.ToLower() == path[pathIdx].ToLower() && path.Length==1) | |
{ | |
path = path.Skip(1).ToArray(); | |
dynamic objectToReturn = expandoList[i].Item2; | |
//return value | |
return new Tuple<bool, dynamic>(true, objectToReturn); | |
} | |
//This is the right path to the item we want | |
else if (expandoList[i].Item1.ToLower() == path[pathIdx].ToLower()) | |
{ | |
path = path.Skip(1).ToArray(); | |
ExpandoObject objectToSearch = expandoList[i].Item2; | |
//Parse again | |
return ParseExpandoForValue(objectToSearch, path); | |
} | |
} | |
return new Tuple<bool, dynamic>(false, null); | |
} | |
private static List<Tuple<string, dynamic>> BuildExpandoList(ExpandoObject expando) | |
{ | |
List<Tuple<string, dynamic>> list = new List<Tuple<string, dynamic>>(); | |
foreach (var item in (dynamic)expando) | |
{ | |
list.Add(new Tuple<string, dynamic>(item.Key, item.Value)); | |
} | |
return list; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment