using System; using System.Collections; using System.Collections.Generic; using System.Dynamic; using System.Linq; using System.Reflection; namespace DynamicParser { public static class ExpandoParser { public static ExpandoObject ToExpando(in object @object) { var properties = @object.GetType().GetProperties(); IDictionary expando = new ExpandoObject(); foreach (var property in properties) { var value = GetValueOrExpandoObject(@object, property); expando.Add(property.Name, value); } return (ExpandoObject)expando; } private static object GetValueOrExpandoObject(in object @object, in PropertyInfo property) { var value = property.GetValue(@object); if (value == null) return null; var valueType = value.GetType(); if (valueType.IsValueType || value is string) return value; if (value is IList && value.GetType().IsGenericType && value.GetType().GetGenericTypeDefinition().IsAssignableFrom(typeof(List<>))) return ToExpandoCollection(value as IList); if (value is IDictionary && value.GetType().IsGenericType && value.GetType().GetGenericTypeDefinition().IsAssignableFrom(typeof(Dictionary<,>))) return ToExpandoCollection(value as IDictionary); return ToExpando(value); } private static List ToExpandoCollection(in IList enumerable) { var list = new List(); foreach (object obj in enumerable) { if (obj.GetType().IsValueType || obj is string) list.Add(obj); else list.Add(ToExpando(obj)); } return list; } private static Dictionary ToExpandoCollection(in IDictionary enumerable) { var dict = new Dictionary(); foreach (var key in enumerable.Keys) { var value = enumerable[key]; if (value.GetType().IsValueType || value is string) dict.Add(key, value); else dict.Add(key, ToExpando(value)); } return dict; } public static void PrintDynamic(in ExpandoObject data, in string prefix = "", in Type type = null) { var dict = new Dictionary(); foreach (var pair in data) { dict.Add(pair.Key, pair.Value); } PrintDict(dict, prefix, type); } private static void PrintDict(in Dictionary data, string prefix = "", in Type type = null) { if (type != null) { Console.WriteLine(prefix + type); prefix += "\t"; } foreach (var element in data) { Console.WriteLine($"{prefix}{element.Key}: {element.Value ?? "null"}"); if (element.Value is null) continue; if (element.Value.GetType() == typeof(ExpandoObject)) { PrintDynamic(element.Value as ExpandoObject, prefix + "\t"); } if (element.Value.GetType() == typeof(List)) { var list = element.Value as List ?? new List(); foreach (var e in list) { if (e.GetType() == typeof(ExpandoObject)) PrintDynamic(e as ExpandoObject, prefix + "\t", e.GetType()); else Console.WriteLine(prefix + "\t" + e); } } if (element.Value.GetType() == typeof(Dictionary)) { PrintDict(element.Value as Dictionary, prefix + "\t"); } } } public static T ToObject(in ExpandoObject @object) where T : new() { T result = Activator.CreateInstance(); var properties = result.GetType().GetProperties(); foreach (var property in properties) { property.SetValue(result, GetValue(@object, property)); } return result; } private static object ToObject(in Type type, in ExpandoObject @object) { var result = Activator.CreateInstance(type); var properties = type.GetProperties(); foreach (var property in properties) { property.SetValue(result, GetValue(@object, property)); } return result; } private static object GetValue(in ExpandoObject @object, in PropertyInfo property) { var value = ((IDictionary)@object)[property.Name]; if (value == null) return default; var vType = value.GetType(); if (vType.IsValueType || value is string) return value; var expandoType = typeof(ExpandoObject); if (vType == expandoType) { return ToObject(property.PropertyType, value as ExpandoObject); } if (vType == typeof(List)) { var list = value as List ?? new List(); var type = property.PropertyType.GenericTypeArguments[0]; var enumerableType = typeof(Enumerable); var castMethod = enumerableType.GetMethod(nameof(Enumerable.Cast))?.MakeGenericMethod(type); var toListMethod = enumerableType.GetMethod(nameof(Enumerable.ToList))?.MakeGenericMethod(type); IEnumerable itemsToCast; itemsToCast = list.Select(item => { if (item.GetType() == expandoType) return ToObject(type, item as ExpandoObject); return Convert.ChangeType(item, type); }); var castedItems = castMethod.Invoke(null, new[] { itemsToCast }); return toListMethod.Invoke(null, new[] { castedItems }); } if (vType == typeof(Dictionary)) { var dict = value as Dictionary ?? new Dictionary(); var keyType = property.PropertyType.GenericTypeArguments[0]; var valueType = property.PropertyType.GenericTypeArguments[1]; var dictType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); var output = Activator.CreateInstance(dictType) as IDictionary ?? new Dictionary(); foreach (var pair in dict) { object k; object v; if (pair.Key.GetType() == expandoType) k = ToObject(keyType, pair.Key as ExpandoObject); else k = Convert.ChangeType(pair.Key, keyType); if (pair.Value.GetType() == expandoType) v = ToObject(valueType, pair.Value as ExpandoObject); else v = Convert.ChangeType(pair.Value, valueType); output.Add(k, v); } return output; } return default; } } }