using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace zero.Core.Utils { public class ObjectTraverser { private const string SEPARATOR = "."; private const string SQBKT_OPEN = "["; private const string SQBKT_CLOSE = "]"; private const string NEWTONSOFT_NS = "Newtonsoft.Json"; /// /// Find all instances of a given type in an object (features nested + enumerable content) /// public static List> Find(object value) where T : class { HashSet exploredObjects = new HashSet(); List> found = new List>(); Find(value, null, String.Empty, null, exploredObjects, found); return found; } /// /// Find all instances of a given type in an object (features nested + enumerable content) /// public static List Find(Type type, object value) { HashSet exploredObjects = new HashSet(); List found = new List(); Find(type, value, null, String.Empty, null, exploredObjects, found); return found; } /// /// Find all instances of a given type in an object (features nested + enumerable content) /// public static List> FindAttribute(object value) where T : Attribute { HashSet exploredObjects = new HashSet(); List> found = new List>(); FindAttribute(value, null, String.Empty, null, exploredObjects, found); return found; } /// /// Recursively ind all instances of a given type in an object /// private static void Find(object value, object parent, string key, PropertyInfo property, HashSet exploredObjects, List> found) where T : class { if (value == null || value is string || exploredObjects.Contains(value) || value.GetType().FullName.StartsWith(NEWTONSOFT_NS)) { return; } exploredObjects.Add(value); System.Collections.ICollection enumerable = value as System.Collections.ICollection; // this property is a list if (enumerable != null) { int idx = 0; foreach (object item in enumerable) { Find(item, value, CombineKey(String.Empty, key, SQBKT_OPEN + idx + SQBKT_CLOSE), property, exploredObjects, found); idx += 1; } return; } // get type of value Type type = typeof(T); Type valueType = value.GetType(); Type genericValueType = valueType.IsGenericType ? valueType.GetGenericTypeDefinition() : null; // check if the current property is our searched type if (valueType == type || genericValueType == type || type.IsAssignableFrom(valueType) || (genericValueType != null && type.IsAssignableFrom(genericValueType))) { found.Add(new Result() { Path = key, Item = value as T, Parent = parent, Property = property }); return; } // check if our property is a primitive type if (value == null || valueType.IsPrimitive || value is string || value is DateTime || value is DateTimeOffset || value is Uri) { return; } // iterate over nested properties PropertyInfo[] properties = valueType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty); foreach (PropertyInfo nestedProperty in properties) { object propertyValue = nestedProperty.GetValue(value, null); Find(propertyValue, value, CombineKey(SEPARATOR, key, nestedProperty.Name), nestedProperty, exploredObjects, found); } } /// /// Recursively ind all instances of a given type in an object /// private static void Find(Type type, object value, object parent, string key, PropertyInfo property, HashSet exploredObjects, List found) { if (value == null || value is string || exploredObjects.Contains(value) || value.GetType().FullName.StartsWith(NEWTONSOFT_NS)) { return; } exploredObjects.Add(value); System.Collections.ICollection enumerable = value as System.Collections.ICollection; // this property is a list if (enumerable != null) { int idx = 0; foreach (object item in enumerable) { Find(type, item, value, CombineKey(String.Empty, key, SQBKT_OPEN + idx + SQBKT_CLOSE), property, exploredObjects, found); idx += 1; } return; } // get type of value Type valueType = value.GetType(); Type genericValueType = valueType.IsGenericType ? valueType.GetGenericTypeDefinition() : null; // check if the current property is our searched type if (valueType == type || genericValueType == type || type.IsAssignableFrom(valueType) || (genericValueType != null && type.IsAssignableFrom(genericValueType))) { found.Add(new Result() { Path = key, Item = value, Type = valueType, Parent = parent, Property = property }); return; } // check if our property is a primitive type if (value == null || valueType.IsPrimitive || value is string || value is DateTime || value is DateTimeOffset || value is Uri) { return; } // iterate over nested properties PropertyInfo[] properties = valueType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty); foreach (PropertyInfo nestedProperty in properties) { object propertyValue = nestedProperty.GetValue(value, null); Find(type, propertyValue, value, CombineKey(SEPARATOR, key, nestedProperty.Name), nestedProperty, exploredObjects, found); } } /// /// Recursively find all instances with the attribute of a given type in an object /// private static void FindAttribute(object value, object parent, string key, PropertyInfo property, HashSet exploredObjects, List> found) where T : Attribute { if (exploredObjects.Contains(key) || (value != null && value.GetType().FullName.StartsWith(NEWTONSOFT_NS))) { return; } exploredObjects.Add(key); // check if the current property contains the attribute if (property != null) { T attribute = property.GetCustomAttribute(true); if (attribute != null) { found.Add(new Result() { Path = key, Item = attribute, Parent = parent, Property = property //parent.GetType().GetProperty(key) }); return; } } System.Collections.ICollection enumerable = value as System.Collections.ICollection; // this property is a list if (enumerable != null && !(value is string)) { int idx = 0; foreach (object item in enumerable) { FindAttribute(item, value, CombineKey(String.Empty, key, SQBKT_OPEN + idx + SQBKT_CLOSE), null, exploredObjects, found); idx += 1; } return; } // check if our property is a primitive type if (value == null || value.GetType().IsPrimitive || value is string || value is DateTime || value is DateTimeOffset || value is Uri) { return; } // iterate over nested properties PropertyInfo[] properties = value.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty); foreach (PropertyInfo nestedProperty in properties) { object propertyValue = nestedProperty.GetValue(value, null); FindAttribute(propertyValue, value, CombineKey(SEPARATOR, key, nestedProperty.Name), nestedProperty, exploredObjects, found); } } private static string CombineKey(string sep, string key1, string key2) { if (String.IsNullOrEmpty(key1)) { return key2; } return String.Join(sep, key1, key2); } public class Result { public string Path { get; set; } public PropertyInfo Property { get; set; } public object Parent { get; set; } public T Item { get; set; } } public class Result { public string Path { get; set; } public PropertyInfo Property { get; set; } public object Parent { get; set; } public object Item { get; set; } public Type Type { get; set; } public int Index { get; set; } } } }