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;
}
