Skip to content

Instantly share code, notes, and snippets.

@ashworth-zach
Last active August 16, 2019 15:47
Show Gist options
  • Save ashworth-zach/200730666d23a2a2c782af1cb5675fd8 to your computer and use it in GitHub Desktop.
Save ashworth-zach/200730666d23a2a2c782af1cb5675fd8 to your computer and use it in GitHub Desktop.
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