I'm trying to loop through a DetailClass objects inside a List using reflection just like for string fields, but I can't figure out how.
class DetailClass
{
public string FieldDetail1 { get; set; }
public string FieldDetail2 { get; set; }
public string FieldDetail3 { get; set; }
}
class SomeClass
{
public string Field1 { get; set; }
public string Field2 { get; set; }
public string Field3 { get; set; }
public List<DetailClass> Artikli { get; set; }
}
private static PropertyInfo[] GetProperties(object obj)
{
return obj.GetType().GetProperties();
}
var myData = new SomeClass();
var prop = GetProperties(myData);
foreach (var item in prop)
{
if (item.PropertyType == typeof(string))
{
var name = item.Name,
var value = item.GetValue(myData).ToString()));
}
//how to get name and value for data inside List<DetailClass>?
}
You were trying to enumerate properties of the parent class
GetValue needs the a reference to the class you are dealing with
Code
var myData = new SomeClass();
myData.Artikli = new List<DetailClass>() { new DetailClass() { FieldDetail1 = "asd", FieldDetail2 = "sdfd", FieldDetail3 = "sdfsg" } };
foreach (var obj in myData.Artikli)
{
foreach (var item in obj.GetType().GetProperties())
{
if (item.PropertyType == typeof(string))
{
var name = item.Name;
var val = item.GetValue(obj);
Console.WriteLine(name + ", " + val);
}
}
}
Demo Here
Additional Resources
PropertyInfo.GetValue Method (Object)
Returns the property value of a specified object.
Parameters
obj
Type: System.Object
The object whose property value will be returned.
You can use your method recursively to get inside all layer of properties
You can check if
item.PropertyType.GetInterfaces().Contains(typeof(IEnumerable))
and if true cast (IEnumerable)item.GetValue(myData) and iterate on the result
recursively.
Just like TheDude answered, you can use a recursive method like so;
private void Recursion(object obj)
{
var props = GetProperties(obj);
foreach (var item in props)
{
if (item.PropertyType == typeof(string))
{
var name = item.Name;
var value = item.GetValue(obj)?.ToString();
}
else if (item.PropertyType == typeof(List<DetailClass>))
{
var test = (List<DetailClass>) item.GetValue(obj);
foreach (var t in test)
{
Recursion(t);
}
}
}
}
And do whatever you want with the name and values in the list.
Related
I need to write a function which takes whichever Object as a parameter, iterate through its properties and write it all to the console. Here is an example:
Equipment.cs
public class Equipment
{
public string SerialNo { get; set; }
public string ModelName { get; set; }
}
People.cs
public class People
{
public string Name { get; set; }
public string Age{ get; set; }
}
Here is example of what I return to above models from API:
var equipment_res = responseObject?.items?.Select(s => new Equipment
{
SerialNo = s.serial_number,
ModelName = s.model.name,
});
var people_res = responseObject?.items?.Select(s => new Equipment
{
SerialNo = s.serial_number,
ModelName = s.model.name,
});
And now I'm struggling to write a function which can take any object and writes its properties to the console. I don't know how to properly pass objects to function in this case:
public void WriteProps(Object obj1, Object obj2)
{
foreach (Object obj1 in obj2)
{
Object obj1 = new Object();
foreach (PropertyInfo p in obj1)
{
Console.WriteLine(p.Name);
Console.WriteLine(p.GetValue(obj1, null));
}
}
}
Function call:
WriteProps(Equipment, equipment_res)
EDIT: below there's a working example but when I explicitly pass named object. It works fine but now I would like to make this function more genric:
foreach (Equipment item in equipment)
{
Equipment eq = new Equipment();
eq = item;
foreach (PropertyInfo p in eq)
{
Console.WriteLine(p.Name);
Console.WriteLine(p.GetValue(eq, null));
}
}
make your method generic and then use reflection (System.Reflection):
void WriteProps<T>(T obj)
{
foreach (var prop in typeof(T).GetProperties())
{
Console.WriteLine(prop.Name);
Console.WriteLine(prop.GetValue(obj));
}
}
Use:
WriteProps(new People
{
Name = "Test",
Age = "11"
});
WriteProps(new Equipment
{
ModelName = "test",
SerialNo = "test"
});
UPDATE:
I'd add this method to work with IEnumerable objects:
void WritePropsList<T>(IEnumerable<T> objects)
{
foreach (var obj in objects)
{
WriteProps(obj);
}
}
I have a problem fetching object from the array object that I made. It seems it didn't fetch the object see my code below:
Product Model
public class Product
{
public string Id { get; set; }
public List<ExcelName> ShortDesc { get; set; } // I want to get the object from here
}
Short Description Model
// get this object and the properties inside it.
public class ExcelName
{
public string Name { get; set; }
public string Language { get; set; }
}
My Code
private static T SetValue<T>(Dictionary<string, object> objectValues)
{
var type = typeof(T);
var objInstance = Activator.CreateInstance(type);
if (!type.IsClass) return default;
foreach (var value in objectValues)
{
if (value.Key.Contains(":Language="))
{
var propName = value.Key.Split(':')[0];
// propName is ShortDesc object
var propInfo = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(e => e.Name.ToLower() == propName.ToLower().Replace(" ", ""));
if (propInfo == null) continue;
if (propInfo.PropertyType.IsGenericType)
{
// I want to get the type and properties from T generic using reflection instead static
var name = typeof(ExcelName);
var excelNameObjectInstance = Activator.CreateInstance(name);
foreach (var propertyInfo in name.GetProperties())
{
propertyInfo.SetValue(excelNameObjectInstance, value.Value, null);
}
// add excelNameObjectInstance object to the list in ShortDesc
}
}
}
}
How to fetch the object from the list of ShortDesc to get the ExcelName objects.
I'm not quite sure what you're trying to do, but it seems like you want a function that instantiates a T and sets its properties according to a dictionary.
Half your code doesn't make sense to me, but my guess is correct, you shouldn't need anything more complicated than this:
private static T SetValue<T>(Dictionary<string, object> objectValues) where T : class, new()
{
var type = typeof(T);
var instance = new T();
foreach (var entry in objectValues)
{
type.GetProperty(entry.Key).SetValue(instance, entry.Value);
}
return instance;
}
If you don't expect the keys to be an exact match for property names, you can introduce a normalization function:
private static string Normalize(string input)
{
return input.ToLower().Replace(" ", "");
}
private static T SetValue<T>(Dictionary<string, object> objectValues) where T : class, new()
{
var type = typeof(T);
var instance = new T();
foreach (var entry in objectValues)
{
var prop = type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.First( x => Normalize(x.Name) == Normalize(entry.Key) );
prop.SetValue(instance, entry.Value);
}
return instance;
}
I have the following model that has these fields:
[Key]
public string Id { get; set; }
[IsSearchable]
public string Code{ get; set; }
[IsSearchable]
public string Name { get; set; }
[IsSearchable]
public string Address { get; set; }
[IsSearchable]
public string PostCode { get; set; }
[IsFilterable]
public int? Setting{ get; set; }
[IsFilterable, IsSortable]
public Location Location { get; set; }
I am writing a method to compare whether values in a database match this model. So far it looks like this:
private bool CompareEquality(Index resultBody, Type indexType)
{
var properties = indexType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
List<PropertyInfo> searchableProperties = new List<PropertyInfo>();
List<PropertyInfo> filterableProperties = new List<PropertyInfo>();
List<PropertyInfo> sortableProperties = new List<PropertyInfo>();
if (properties.Count() == resultBody.Fields.Count)
{
foreach (var property in properties)
{
var isSearchableAttribute = property.GetCustomAttribute<IsSearchableAttribute>();
var isFilterableAttribute = property.GetCustomAttribute<IsFilterableAttribute>();
var isSortableAttribute = property.GetCustomAttribute<IsSortableAttribute>();
if (isSearchableAttribute != null)
{
searchableProperties.Add(property);
}
if (isFilterableAttribute != null)
{
filterableProperties.Add(property);
}
if (isSortableAttribute != null)
{
sortableProperties.Add(property);
}
}
CheckAttributeEquality(searchableProperties, filterableProperties, sortableProperties);
}
return false;
}
The CheckAttributeEquality method:
private bool CheckAttributeEquality(List<PropertyInfo> searchableProperties, List<PropertyInfo> filterableProperties, List<PropertyInfo> sortableProperties)
{
if (searchableProperties.Count == 4 && filterableProperties.Count == 2 && sortableProperties.Count == 1)
{
CheckPropertyFields(searchableProperties, filterableProperties, sortableProperties);
return true;
}
return false;
}
As I started to write a method to check that the field names match, like so:
foreach (var property in searchableProperties)
{
if (property.Name == "Id" ||)
{
...
}
if (property.Name == "Code")
{
...
}
// etc
I realised how messy and long-winded this whole approach is. I am not hugely experienced in C# and would appreciate any advice as to how I can refactor this up a little bit? I want to check for attribute and name matches.
you could use the Typedescriptor (using System.ComponentModel) for that. Try this:
var pdc = TypeDescriptor.GetProperties( this ); //or your model object, if its not "this"
foreach (var property in searchableProperties)
{
var descriptors = pdc[ property.Name ];
// check if your searchable descriptor is there, and do error handling
}
Once it works, you could also try to solve it with LINQ.
I have a class, which is created and populated from an xml string, I've simplified it for example purposes:
[XmlRoot("Person")]
public sealed class Person
{
[XmlElement("Name")]
public string Name { get; set; }
[XmlElement("Location")]
public string Location { get; set; }
[XmlElement("Emails", Type = typeof(PersonEmails)]
public PersonEmails Emails { get; set; }
}
public class PersonEmails
{
[XmlElement("Email", Type = typeof(PersonEmail))]
public PersonEmail[] Emails { get; set; }
}
public class PersonEmail
{
[XmlAttribute("Type")]
public string Type { get; set; }
[XmlText]
public string Value { get; set; }
}
To extract the information, I'm trying to load them into another class, which is simply:
public class TransferObject
{
public string Name { get; set; }
public ObjectField[] Fields { get; set; }
}
public class ObjectField
{
public string Name { get; set; }
public string Value { get; set; }
}
I'm only populating "Fields" from the other object, which would simply be (Name = "Location", Value = "London"), but for Emails, (Name = "Email"+Type, Value = jeff#here.com)
Currently I can populate all the other fields, but I'm stuck with Emails, and knowing how to dig deep enough to be able to use reflection (or not) to get the information I need. Currently I'm using:
Person person = Person.FromXmlString(xmlString);
List<ObjectField> fields = new List<ObjectField>();
foreach (PropertyInfo pinfo in person.getType().GetProperties()
{
fields.Add(new ObjectField { Name = pinfo.Name, Value = pinfo.getValue(person, null).ToString();
}
How can I expand on the above to add all my emails to the list?
You are trying to type cast a complex values type to string value so you lost the data. Instead use following code:
class Program
{
static void Main(string[] args)
{
Person person = new Person();
person.Name = "Person One";
person.Location = "India";
person.Emails = new PersonEmails();
person.Phones = new PersonPhones();
person.Emails.Emails = new PersonEmail[] { new PersonEmail() { Type = "Official", Value = "xyz#official.com" }, new PersonEmail() { Type = "Personal", Value = "xyz#personal.com" } };
person.Phones.Phones = new PersonPhone[] { new PersonPhone() { Type = "Official", Value = "789-456-1230" }, new PersonPhone() { Type = "Personal", Value = "123-456-7890" } };
List<ObjectField> fields = new List<ObjectField>();
fields = GetPropertyValues(person);
}
static List<ObjectField> GetPropertyValues(object obj)
{
List<ObjectField> propList = new List<ObjectField>();
foreach (PropertyInfo pinfo in obj.GetType().GetProperties())
{
var value = pinfo.GetValue(obj, null);
if (pinfo.PropertyType.IsArray)
{
var arr = value as object[];
for (var i = 0; i < arr.Length; i++)
{
if (arr[i].GetType().IsPrimitive)
{
propList.Add(new ObjectField() { Name = pinfo.Name + i.ToString(), Value = arr[i].ToString() });
}
else
{
var lst = GetPropertyValues(arr[i]);
if (lst != null && lst.Count > 0)
propList.AddRange(lst);
}
}
}
else
{
if (pinfo.PropertyType.IsPrimitive || value.GetType() == typeof(string))
{
propList.Add(new ObjectField() { Name = pinfo.Name, Value = value.ToString() });
}
else
{
var lst = GetPropertyValues(value);
if (lst != null && lst.Count > 0)
propList.AddRange(lst);
}
}
}
return propList;
}
}
Check this snippet out:
if(pinfo.PropertyType.IsArray)
{
// Grab the actual instance of the array.
// We'll have to use it in a few spots.
var array = pinfo.GetValue(personObject);
// Get the length of the array and build an indexArray.
int length = (int)pinfo.PropertyType.GetProperty("Length").GetValue(array);
// Get the "GetValue" method so we can extact the array values
var getValue = findGetValue(pinfo.PropertyType);
// Cycle through each index and use our "getValue" to fetch the value from the array.
for(int i=0; i<length; i++)
fields.Add(new ObjectField { Name = pinfo.Name, Value = getValue.Invoke(array, new object[]{i}).ToString();
}
// Looks for the "GetValue(int index)" MethodInfo.
private static System.Reflection.MethodInfo findGetValue(Type t)
{
return (from mi in t.GetMethods()
where mi.Name == "GetValue"
let parms = mi.GetParameters()
where parms.Length == 1
from p in parms
where p.ParameterType == typeof(int)
select mi).First();
}
You can definately do it with Reflection... You can take advantage of the fact that a Type can tell you if it's an array or not (IsArray)... and then take advantage of the fact that an Array has a method GetValue(int index) that will give you a value back.
Per your comment
Because Emails is a property within a different class, recursion should be used. However the trick is knowing when to go to the next level. Really that is up to you, but
if it were me, I would use some sort of Attribute:
static void fetchProperties(Object instance, List<ObjectField> fields)
{
foreach(var pinfo in instance.GetType().GetProperties())
{
if(pinfo.PropertyType.IsArray)
{
... // Code described above
}
else if(pinfo.PropertyType.GetCustomAttributes(typeof(SomeAttribute), false).Any())
// Go the next level
fetchProperties(pinfo.GetValue(instance), fields);
else
{
... // Do normal code
}
}
}
Maybe a silly question, I can read all the properties from list parameter but not the value in the fields of <T>.
This is the structure
public class TestRecord {
public string StringTest { get; set; }
public int IntegerTest { get; set; }
public DateTime DateTimeTest { get; set; }
}
The generic method
public void TestOfT<T>(List<T> pList) where T:class, new() {
T xt = (T)Activator.CreateInstance(typeof(T));
foreach (var tp in pList[0].GetType().GetProperties()) {
// System.Reflection.PropertyInfo pi = xt.GetType().GetProperty("StringTest");
// object s = pi.GetValue(tp, null) ; -- failed
Debug.WriteLine(tp.Name);
Debug.WriteLine(tp.PropertyType);
Debug.WriteLine(tp.GetType().Name);
}
}
Test code for generic method
public void TestTCode() {
List<TestRecord> rec = new List<TestRecord>();
rec.Add(new TestRecord() {
StringTest = "string",
IntegerTest = 1,
DateTimeTest = DateTime.Now
});
TestOfT<TestRecord>(rec);
}
Thanks for your help.
the problem is you are reading the value from the new instance (which can be written simply as var xt = new T();.
if you want to get the property of the item you need to pull the value from the instance.
void TestOfT<T>(IEnumerable<T> list) where T: class, new()
{
var properties = typeof(T).GetProperties();
foreach (var item in list)
foreach (var property in properties)
{
var name = property.Name;
var value = property.GetValue(item, null);
Debug.WriteLine("{0} is {1}", name, value);
}
}
public void TestOfT<T>(List<T> pList) where T:class, new() {
var xt = Activator.CreateInstance(typeof(T));
foreach (var tp in pList[0].GetType().GetProperties()) {
Debug.WriteLine(tp.Name);
Debug.WriteLine(tp.PropertyType);
Debug.WriteLine(tp.GetType().Name);
Debug.WriteLine(tp.GetValue(pList[0], null));
}
}