This question already has answers here:
How to recursively print the values of an object's properties using reflection
(4 answers)
Closed 9 years ago.
I came over this site (http://snipplr.com/view.php?codeview&id=17637), which illustrates use of reflection like this:
public class Person
{
public int Age { get; set; }
public string Name { get; set; }
}
private void button2_Click_1(object sender, EventArgs e)
{
var person = new Person { Age = 30, Name = "Tony Montana" };
var properties = typeof(Person).GetProperties();
foreach (PropertyInfo property in properties)
{
Console.WriteLine("{0} = {1}", property.Name, property.GetValue(person, null));
}
}
The codesnippet above will give you:
Age: 30
Name: Tony Montana
What if we added "Kid" to the class "AnotherPerson" like this
public class Kid
{
public int KidAge { get; set; }
public string KidName { get; set; }
}
public class AnotherPerson
{
public int Age { get; set; }
public string Name { get; set; }
public Kid Kid { get; set; }
}
This snippet;
private void button3_Click(object sender, EventArgs e)
{
var anotherPerson = new AnotherPerson { Age = 30, Name = "Tony Montana", Kid = new Kid { KidAge = 10, KidName = "SomeName" } };
var properties = typeof(AnotherPerson).GetProperties();
foreach (PropertyInfo property in properties)
{
Console.WriteLine("{0} = {1}", property.Name, property.GetValue(anotherPerson, null));
}
}
gives me:
Age: 30
Name: Tony Montana
Kid: ProjectName.Form1+Kid
Not quite what I was looking for.... Could I use reflection to iterate trough "Kid" also? Suggestions?
Could have a helper method which is something like this?
public List<KeyValuePair<string, object>> GetPropertiesRecursive(object instance)
{
List<KeyValuePair<string, object>> propValues = new List<KeyValuePair<string, object>>();
foreach(PropertyInfo prop in instance.GetType().GetProperties())
{
propValues.Add(new KeyValuePair<string, object>(prop.Name, prop.GetValue(instance, null));
propValues.AddRange(GetPropertiesRecursive(prop.GetValue(instance));
}
}
Call as - GetPropertiesRecursive(anotherPerson)
var anotherPerson = new AnotherPerson { Age = 30, Name = "Tony Montana", Kid = new Kid { KidAge = 10, KidName = "SomeName" } };
var properties = typeof(AnotherPerson).GetProperties();
foreach (PropertyInfo property in properties)
{
if (property.PropertyType == typeof(Kid)){
var pp = typeof(Kid).GetProperties();
Console.WriteLine("Kid:");
foreach (PropertyInfo p in pp)
{
Console.WriteLine("\t{0} = {1}", p.Name, p.GetValue(anotherPerson.Kid, null));
}
}
else
Console.WriteLine("{0} = {1}", property.Name, property.GetValue(anotherPerson, null));
}
The code loops through the properties and uses ToString (through the String.Format method that Console.WriteLine uses) to get the string representation of the property values.
If you want to change how the Kid property value is displayed, you basically have two options:
Check the type of the property (property.PropertyType) in the reflection code, so that you can do something different for the Kid type.
Override the ToString method in the Kid class, so that it returns whatever string you want to display as value.
A method doing some recursion can get the values for properties within properties:
public static string GetProperties(object obj)
{
var t = obj.GetType();
var properties = t.GetProperties();
var s = "";
foreach (PropertyInfo property in properties)
{
object propValue = property.GetValue(obj, new object[0]);
string propertyToString;
if (t.GetMethod("ToString").DeclaringType != typeof(object))
propertyToString = propValue.ToString();
else if (typeof(IConvertible).IsAssignableFrom(property.PropertyType))
propertyToString = Convert.ToString(propValue);
else
propertyToString = GetProperties(propValue);
s += string.Format("'{0}' = '{1}'", property.Name, propertyToString);
}
return s;
}
If you had practical purposes in mind, JSON (or other) serialization may be more appropriate, e.g. using JSON.net would produce a similar string.
Related
Currently, am adding the properties and values to the object manually like this example and sending to Dapper.SimpleCRUD to fetch data from Dapper Orm. This is the desired output I would like to achieve.
object whereCriteria = null;
whereCriteria = new
{
CountryId = 2,
CountryName = "Anywhere on Earth",
CountryCode = "AOE",
IsActive = true
};
The following class should build the object in the above mentioned format and return the ready-made object.
public static class WhereClauseBuilder
{
public static object BuildWhereClause(object model)
{
object whereObject = null;
var properties = GetProperties(model);
foreach (var property in properties)
{
var value = GetValue(property, model);
//Want to whereObject according to the property and value. Need help in this part!!!
}
return whereObject;
}
private static object GetValue(PropertyInfo property, object model)
{
return property.GetValue(model);
}
private static IEnumerable<PropertyInfo> GetProperties(object model)
{
return model.GetType().GetProperties();
}
}
This function WhereClauseBuilder.BuildWhereClause(object model) should return the object in expected format (mentiond above). Here is the implementation of how I would like to use.
public sealed class CountryModel
{
public int CountryId { get; set; }
public string CountryName { get; set; }
public string CountryCode { get; set; }
public bool IsActive { get; set; }
}
public class WhereClauseClass
{
public WhereClauseClass()
{
var model = new CountryModel()
{
CountryCode = "AOE",
CountryId = 2,
CountryName = "Anywhere on Earth",
IsActive = true
};
//Currently, won't return the correct object because the implementation is missing.
var whereClauseObject = WhereClauseBuilder.BuildWhereClause(model);
}
}
Maybe something like that:
private const string CodeTemplate = #"
namespace XXXX
{
public class Surrogate
{
##code##
}
}";
public static Type CreateSurrogate(IEnumerable<PropertyInfo> properties)
{
var compiler = new CSharpCodeProvider();
var compilerParameters = new CompilerParameters { GenerateInMemory = true };
foreach (var item in AppDomain.CurrentDomain.GetAssemblies().Where(x => !x.IsDynamic))
{
compilerParameters.ReferencedAssemblies.Add(item.Location);
}
var propertiesCode =
string.join("\n\n", from pi in properties
select "public " + pi.PropertyType.Name + " " + pi.Name + " { get; set; }");
var source = CodeTemplate.Replace("##code##", propertiesCode);
var compilerResult = compiler.CompileAssemblyFromSource(compilerParameters, source);
if (compilerResult.Errors.HasErrors)
{
throw new InvalidOperationException(string.Format("Surrogate compilation error: {0}", string.Join("\n", compilerResult.Errors.Cast<CompilerError>())));
}
return compilerResult.CompiledAssembly.GetTypes().First(x => x.Name == "Surrogate");
}
And now use it:
public static object BuildWhereClause(object model)
{
var properties = GetProperties(model);
var surrogateType = CreateSurrogate(properties);
var result = Activator.CreateInstance(surrogateType);
foreach (var property in properties)
{
var value = GetValue(property, model);
var targetProperty = surrogateType.GetProperty(property.Name);
targetProperty.SetValue(result, value, null);
}
return result;
}
I didn't compile that. It's only written here. Maybe there are some errors. :-)
EDIT:
To use ExpandoObject you can try this:
public static object BuildWhereClause(object model)
{
var properties = GetProperties(model);
var result = (IDictionary<string, object>)new ExpandoObject();
foreach (var property in properties)
{
var value = GetValue(property, model);
result.Add(property.Name, value);
}
return result;
}
But I don't know whether this will work for you.
Is there an automagic (automapper?) way to map an entity to a runtime created dynamic object with properties passed as parameters? I want to do an API where the clients can select the properties of the entities they want to fetch.
I mean:
class Patient
{
public int PatientId{ get; set; }
public string Name{ get; set; }
public string LastName{ get; set; }
public string Address{ get; set; }
...
}
getPatient(string[] properties)
{
//Return an object that has the properties passed as parameters
}
Imagine you only want to fetch a PatientDTO with PatientId and Name:
getPatient(new string[]{"PatientId", "Name"}
should return
{
"PatientId": "1234",
"Name": "Martin",
}
and so on.
For now I'm doing it with Dictionary, but probably there is a better way. This is my approach:
For a single object:
public static Dictionary<string, object> getDTO(string[] properties, T entity)
{
var dto = new Dictionary<string, object>();
foreach (string s in properties)
{
dto.Add(s, typeof(T).GetProperty(s).GetValue(entity));
}
return dto;
}
For a list of objects:
public static List<Dictionary<string, object>> getDTOList(string[] properties, List<T> TList)
{
var dtoList = new List<Dictionary<string, object>>();
foreach(T entity in TList)
{
dtoList.Add(getDTO(properties, entity));
}
return dtoList;
}
Thank you.
How about creating a new dynamic object based solely on the specified property fields and returning the dynamic?
You will need to add using statements for: System.Dynamic and System.ComponentModel for the following to work.
public static dynamic getDTO(object entity, string[] properties)
{
IDictionary<string, object> expando = new ExpandoObject();
foreach (var p in properties)
{
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(entity.GetType()))
{
if (property.Name == p)
{
expando.Add(p, property.GetValue(entity));
break;
}
}
}
return expando as ExpandoObject;
}
Calling this method would look something like this:
var patient = new Patient() { PatientId = 1, Name = "Joe", LastName = "Smith", Address = "123 Some Street\nIn Some Town, FL 32333" };
var propList = new string[] { "PatientId", "Name", "Address" };
dynamic result = getDTO(patient, propList);
Console.WriteLine("Id:{0} Name: {1}\nAddress: {2}", result.PatientId, result.Name, result.Address);
Console.ReadLine();
I'd like return an object (ExpandoObject) with only the fields and nested fields received by the method.
var fieldsToGet = new List<string> { "FirstName", "Id"};
When I do this :
.Select(x => Helpers.FilteringProperties(x, fieldsToGet))
I receive an object with this two values, that's work.
I receive an object with FirstName and Id
Now I'd like return some properties of the nested object :
var fieldsToGet = new List<string> { "FirstName", "Id", "Language.Name"};
I'd like receive these properties :
FirstName, Id and Language.Name
The code below works but I'd like stay generic enough and be able to manage the nested.
How can I do this generic, managed the nested object ?
Thanks,
The current code :
public static object FilteringProperties(object employee, List<string> fields)
{
if (!fields.Any())
return employee;
else
{
ExpandoObject result = new ExpandoObject();
foreach (var field in fields)
{
var fieldValue = employee.GetType()
.GetProperty(field, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance)
.GetValue(employee, null);
((IDictionary<String, Object>)result).Add(field, fieldValue);
}
return result;
}
}
Sample classes :
public class Employee
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Language Language { get; set; }
public int LanguageId { get; set; }
}
public class Language
{
public int Id { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
I suppose you could do a split on the "."-character, get the Property with that name, and then get its value by calling your own function recursively.
Something like this (pseudo-code, can be a lot better)
if (field.Contains(".")) {
var parts = field.Split('.');
var fieldName = parts[0];
List<string> toGet = new List<string>();
toGet.Add(parts[1]); // this now contains everything after the "."
var fieldValue = employee.GetType()
.GetProperty(fieldName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance)
.GetValue(employee, null);
((IDictionary<String, Object>)result).Add(field, FilteringProperties(fieldValue, toGet)
}
Using a recursive approach. I'm sure it can be improved.
public static object FilteringProperties(object employee, List<string> fields)
{
if (!fields.Any())
return employee;
else
{
ExpandoObject result = new ExpandoObject();
foreach (var field in fields)
{
object fieldValue = null;
Regex regex = new Regex("(\\w+)\\.(\\w+)");
Match match = regex.Match(field);
if (match.Success)
{
string className = match.Groups[1].Value;
string propertyName = match.Groups[2].Value;
var o = FilteringProperties(employee.GetType().GetProperty(className).GetValue(employee, null), new List<string>() {propertyName});
var entry = (IDictionary<string, object>) o;
fieldValue = entry[propertyName];
}
if(fieldValue == null)
fieldValue = employee.GetType()
.GetProperty(field, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance)
.GetValue(employee, null);
((IDictionary<String, Object>)result).Add(field, fieldValue);
}
return result;
}
}
Something like this seems to work for me -
public static ExpandoObject CreateObject(object parent, List<string> fields)
{
var expando = new ExpandoObject();
var ret = (IDictionary<string,object>)expando;
foreach (var property in fields)
{
//split to determine if we are a nested property.
List<string> properties = property.Split('.').ToList();
if (properties.Count() > 1)
{
//get our 'childs' children - ignoring the first item
var grandChildren = properties.Skip(1);
//copy this child object and use it to pass back into this method recusivly - thus creating our nested structure
var child = parent.GetType().GetProperty(properties[0]).GetValue(parent, null);
//passing in the child object and then its children - which are grandchildren from our parent.
ret.Add(properties[0], CreateObject(child, grandChildren.ToList()));
}
else //no nested properties just assign the property
ret.Add(property, parent.GetType().GetProperty(property).GetValue(parent));
}
return expando;
}
Thanks to Marc - guru from this site - I came up with the following approach:
public static object GetFlattenPropertyValue(this object source, string flattenPropertyName) {
var expression = source.GetType().CreatePropertyExpression(flattenPropertyName);
if (expression != null) {
var getter = expression.Compile();
return getter.DynamicInvoke(source);
}
return null;
}
public static LambdaExpression CreatePropertyExpression(this Type type, string flattenPropertyName) {
if (flattenPropertyName == null) {
return null;
}
var param = Expression.Parameter(type, "x");
Expression body = param;
foreach (var member in flattenPropertyName.Split('.')) {
body = Expression.PropertyOrField(body, member);
}
return Expression.Lambda(body, param);
}
I have a ShowAttribute and I am using this attribute to mark some properties of classes. What I want is, printing the values by the properties that has a Name attribute. How can I do that ?
public class Customer
{
[Show("Name")]
public string FirstName { get; set; }
public string LastName { get; set; }
public Customer(string firstName, string lastName)
{
this.FirstName = firstName;
this.LastName = lastName;
}
}
class ShowAttribute : Attribute
{
public string Name { get; set; }
public ShowAttribute(string name)
{
Name = name;
}
}
I know how to check the property has a ShowAttribute or not, but I couldnt understand how to use it.
var customers = new List<Customer> {
new Customer("Name1", "Surname1"),
new Customer("Name2", "Surname2"),
new Customer("Name3", "Surname3")
};
foreach (var customer in customers)
{
foreach (var property in typeof (Customer).GetProperties())
{
var attributes = property.GetCustomAttributes(true);
if (attributes[0] is ShowAttribute)
{
Console.WriteLine();
}
}
}
Console.WriteLine(property.GetValue(customer).ToString());
However, this will be pretty slow. You can improve that with GetGetMethod and creating a delegate for each property. Or compile an expression tree with a property access expression into a delegate.
You can try the following:
var type = typeof(Customer);
foreach (var prop in type.GetProperties())
{
var attribute = Attribute.GetCustomAttribute(prop, typeof(ShowAttribute)) as ShowAttribute;
if (attribute != null)
{
Console.WriteLine(attribute.Name);
}
}
The output is
Name
If you want the value of the property:
foreach (var customer in customers)
{
foreach (var property in typeof(Customer).GetProperties())
{
var attributes = property.GetCustomAttributes(false);
var attr = Attribute.GetCustomAttribute(property, typeof(ShowAttribute)) as ShowAttribute;
if (attr != null)
{
Console.WriteLine(property.GetValue(customer, null));
}
}
}
And the output is here:
Name1
Name2
Name3
foreach (var customer in customers)
{
foreach (var property in typeof (Customer).GetProperties())
{
if (property.IsDefined(typeof(ShowAttribute))
{
Console.WriteLine(property.GetValue(customer, new object[0]));
}
}
}
Please be aware of performance hit.
I have the following program that loops through all properties of my variable:
class Program
{
static void Main(string[] args)
{
var person = new Person {Age = 30, Name = "Tony Montana", Mf = new Gender {Male = true,Female = false}};
var type = typeof(Person);
var properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
Console.WriteLine("{0} = {1} : Value= {2}", property.Name, property.PropertyType, property.GetValue(person, null));
}
Console.Read();
}
}
public class Person
{
public int Age { get; set; }
public string Name { get; set; }
public Gender Mf;
}
public class Gender
{
public bool Male;
public bool Female;
}
When I run this, I get the following output:
"Age = System.Int32 : Value= 30"
"Name = System.String : Value= Tony Montana"
I dont see my complex type person.Mf. How do i loop through my object person and get the type of person.Mf and the properties of person.Mf (i.e person.Mf.Male etc)? Thanks in advance
Mf is a field, not a property. Change it to:
public Gender Mf { get; set; }
Or, alternatively, use reflection to iterate through all of the public fields (but I think you're better off just making it a property).
You don't see it cause it's not a property.
To resolve that issue,
or define it like a property
or using reflection recover fields too
FieldInfo[] fields = type.GetFields()