I was tasked to build a method that would take in any object and a string that specifies the property to be returned.
Let’s say we have a complex object as followed
public class Company { public string Name { get; set; } public string ABN { get; set; } public int YearFounded { get; set; } public int TotalEmployees { get; set; } public IEnumerable<string> DepartmentNames { get; set; } public IEnumerable<Employee> Employees { get; set; } public Employee Ceo { get; set; } } public class CorporateGroup { public IEnumerable<Company> Companies { get; set; } public string Name { get; set; } public Employee Ceo { get; set; } } public class Employee { public string FirstName { get; set; } public string LastName { get; set; } }
The sample test object is as followed
var corporateGroup = new CorporateGroup { Name = "ABC", Ceo = new Employee { FirstName = "John", LastName = "Nerks" }, Companies = new[] { new Company { Name = "SAMPLE", YearFounded = 2017, TotalEmployees = 10, DepartmentNames = new[] { "IT", "Accounting" }, Employees = new[] { new Employee { FirstName = "QWERTY", LastName = "OPI" }, new Employee { FirstName = "ZXCV", LastName = "BNM" }, new Employee { FirstName = "FGH", LastName = "RTE" }, new Employee { FirstName = "VCB", LastName = "JKG" } }, Ceo = new Employee { FirstName = "HGJ", LastName = "KLJ" }, ABN = "ABCDEFG" }, new Company { Name = "XYZ", YearFounded = 2018, TotalEmployees = 15, DepartmentNames = new[] { "Finance", "Marketing" }, Employees = new[] { new Employee { FirstName = "KLJ", LastName = "SFD" }, new Employee { FirstName = "NMB", LastName = "JKH" } }, Ceo = new Employee { FirstName = "ZXC", LastName = "UYTR" } } } };
So if the property string is “ceo.firstname” then the result should be John. Or a more complex one “companies[].employees[].firstName” should return “QWERTY“, “ZXCV“,“FGH“, “VCB“,“KLJ“, and “NMB“.
I decided to create an extension method for type object. Below is my implementation
public static object GetPropertyValue(this object value, string propertyName) { var nameParts = propertyName.Split('.'); var isObjectCollection = false; foreach (var part in nameParts) { if (value == null) { return null; } var propertyPartName = part.Contains("[") ? part.Split('[')[0] : part; var type = value.GetType(); isObjectCollection = isObjectCollection || type.IsArray || type.GenericTypeArguments.Any(); if (isObjectCollection) { value = value.GetCollectionPropertyValue(propertyPartName) } else { var propertyInfo = type.GetProperty(propertyPartName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); if (propertyInfo == null) { return null; } value = propertyInfo.GetValue(value); } } return value; } public static object GetCollectionPropertyValue(this object value, string propertyName) { if (value == null) { return null; } if (((IList)value).Count == 0) { return new List<object>(); } var name = propertyName.Contains("[") ? propertyName.Split('[')[0] : propertyName; var type = ((IList)value)[0].GetType(); var propertyInfo = type.GetProperty(name, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); if (propertyInfo == null) { return null; } var valueList = (from object listItem in (IList)value select propertyInfo.GetValue(listItem)) .Where(x => x != null).ToList(); if (valueList.Any()) { var itemType = valueList[0].GetType(); // Flatten the list if list item is a collection if (itemType.IsArray || itemType.GenericTypeArguments.Any()) { valueList = valueList.SelectMany(x => (from object listItem in (IList)x select listItem)).ToList(); } } return valueList; }