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'm trying fill a list of object from data table using the following method
public static List<T> toList<T>(this DataTable table) where T : new()
{
try
{
List<T> list = new List<T>();
foreach (var row in table.AsEnumerable())
{
var obj = new T();
foreach (var prop in typeof(T).GetProperties())
{
PropertyInfo propertyInfo = obj.GetType().GetProperty(prop.Name);
Type targetType = propertyInfo.PropertyType;
if (table.Columns.Contains(prop.Name))
{
try
{
object value = row[prop.Name];
if (value != null)
{
if (value.GetType() == typeof(string))
{
if (string.IsNullOrWhiteSpace(value.ToString()))
{
value = null;
}
}
if (targetType.IsGenericType && targetType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
targetType = Nullable.GetUnderlyingType(targetType);
}
value = Convert.ChangeType(value, targetType);
propertyInfo.SetValue(obj, value);
}
}
catch
{
continue;
}
}
}
list.Add(obj);
}
return list;
}
catch (Exception ex)
{
return null;
}
}
I've the following models
public class A
{
public string str1 {get;set;}
public int int1 {get;set;}
public DateTime dateTime1 {get;set;}
}
public class B
{
public string str2 {get;set;}
public int int2 {get;set;}
public DateTime dateTime2 {get;set;}
public A vara {get;set;}
}
My Data table looks like this
+-----------+-----------+-----------+-----------+---------------+---------------+
| str1 | str2 | int1 | int2 | dateTime1 | dateTime2 |
+-----------+-----------+-----------+-----------+---------------+---------------+
| "abc" | "def" | 1 | 2 | NULL | NULL |
+-----------+-----------+-----------+-----------+---------------+---------------+
All this works fine If I use
List<B> list = dataTable.toList<B>();
But I also want to set the value of vara in each element of list.
How can I check that if a Type is custom defined type?
I can not use Type.IsClass because it is true for string too.
If I can detect that a property is of Custom Class Type then I can fill that's value using the same method.
I hope that I've explained it well.
I was able create a following generic solution
public static List<T> toList<T>(this DataTable table) where T : new()
{
try
{
var list = table.toList(typeof(T));
var newLIst = list.Cast<T>().ToList();
return newLIst;
}
catch
{
return null;
}
}
public static List<object> toList(this DataTable table, Type type)
{
try
{
List<object> list = new List<object>();
foreach (var row in table.AsEnumerable())
{
var obj = row.toObject(type);
list.Add(obj);
}
return list;
}
catch
{
return null;
}
}
public static object toObject(this DataRow row, Type type, string sourcePropName = "")
{
try
{
var obj = Activator.CreateInstance(type);
var props = type.GetProperties();
foreach (var prop in props)
{
PropertyInfo propertyInfo = type.GetProperty(prop.Name);
Type targetType = propertyInfo.PropertyType;
string propName = prop.Name;
if (!string.IsNullOrWhiteSpace(sourcePropName))
{
propName = sourcePropName + "__" + propName;
if (!row.Table.Columns.Contains(propName))
{
propName = prop.Name;
}
}
if (row.Table.Columns.Contains(propName))
{
try
{
object value = row[propName];
if (value != null)
{
if (value.GetType() == typeof(string))
{
if (string.IsNullOrWhiteSpace(value.ToString()))
{
value = null;
}
}
targetType = targetType.handleNullableType();
value = Convert.ChangeType(value, targetType);
propertyInfo.SetValue(obj, value);
}
}
catch
{
continue;
}
}
else
if (targetType.IsClass && targetType != typeof(string))
{
if (targetType.IsGenericList())
{
Type ltype = targetType.GetProperty("Item").PropertyType;
object value = row.toObject(ltype, propName);
if (value == null)
{
continue;
}
var valList = new List<object> { value }.ConvertList(targetType);
try
{
propertyInfo.SetValue(obj, valList);
}
catch (Exception ex)
{
log.Error(ex);
}
}
else
{
object value = row.toObject(targetType, propName);
propertyInfo.SetValue(obj, value);
}
}
}
return obj;
}
catch
{
return null;
}
}
public static object ConvertList(this List<object> value, Type type)
{
IList list = (IList)Activator.CreateInstance(type);
foreach (var item in value)
{
list.Add(item);
}
return list;
}
In order to fill all properties of class B including vara property, I must prefix the names of columns that belong to vara properties and added splitter ___
Therefore, the about table should be as follows
+-----------------+-----------+-----------------+-----------+---------------------+---------------+
| vara__str1 | str2 | vara__int1 | int2 | vara__dateTime1 | dateTime2 |
+-----------------+-----------+-----------------+-----------+---------------------+---------------+
| "abc" | "def" | 1 | 2 | NULL | NULL |
+-----------------+-----------+-----------------+-----------+---------------------+---------------+
I have the following block:
public abstract class AParsable<T> where T : AParsable<T>
{
public static T Parse(string input)
{
return JSON.TryParse<T>(input);
}
}
which will be used in this way (Edited with full usage intention. TL;DR: parse incoming request body into a request data class.
public static T ParseBody<T>(this System.Web.HttpRequestBase request) where T: new()
{
var instance = new T();
var publicProps = typeof(T).GetProperties();
var stringType = typeof(string);
foreach (var prop in publicProps)
{
var propType = prop.PropertyType;
if (!prop.CanWrite) continue;
var input = request.Form[prop.Name];
if (string.IsNullOrEmpty(input)) continue;
if (propType == typeof(string))
{
prop.SetValue(instance, input, null);
}
else
{
var parse = propType.GetMethod("Parse", BindingFlags.Public | BindingFlags.Static, null, new Type[] { stringType }, null);
if (parse != null) // I always get null here
{
var value = parse.Invoke(null, new object[] { input });
prop.SetValue(instance, value, null);
}
}
}
return instance;
}
I always get parse as null for the following code
public class TreeNodeArray : AParsable<TreeNodeArray>
{
public List<TreeNodeWrapper> nodes { get; set; }
}
Update: Would appreciate it if someone could point out a better way to dynamically parse the request body in .NET MVC into a class instance.
Update 2: In a controller it would be used like this:
internal class MyRequestModel {
public TreeNodeArray selectedNodes { get; set; }
public bool isForAllNodes { get; set; }
}
var requestData = Request.ParseBody<MyRequestModel>();
requestData.selectedNodes == null;
You've omitted BindingFlags.FlattenHierarchy from your sample code (though it's in the title). This needs to be included in your code. Working version:
var parse = propType
.GetMethod("Parse",
BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Static,
null, new Type[] { stringType }, null
);
In my application i have to use ExpandoObject in order to create/delete properties during the runtime; However, i have to map the returned ExpandoObject of a function to the corresponding object/class. So i have came up with a small Mapper that does the job but with 3 problems:
It does not recursively map the inner objects of the ExpandoObject
as supposed.
When i try to map int to a Nullable simply it will throw a type
mismatch because i can't find a way to detect and cast it properly.
Fields can't be mapped public string Property;.
Code:
I- Implementation:
public static class Mapper<T> where T : class
{
#region Properties
private static readonly Dictionary<string, PropertyInfo> PropertyMap;
#endregion
#region Ctor
static Mapper() { PropertyMap = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).ToDictionary(p => p.Name.ToLower(), p => p); }
#endregion
#region Methods
public static void Map(ExpandoObject source, T destination)
{
if (source == null)
throw new ArgumentNullException("source");
if (destination == null)
throw new ArgumentNullException("destination");
foreach (var kv in source)
{
PropertyInfo p;
if (PropertyMap.TryGetValue(kv.Key.ToLower(), out p))
{
Type propType = p.PropertyType;
if (kv.Value == null)
{
if (!propType.IsByRef && propType.Name != "Nullable`1")
{
throw new ArgumentException("not nullable");
}
}
else if (kv.Value.GetType() != propType)
{
throw new ArgumentException("type mismatch");
}
p.SetValue(destination, kv.Value, null);
}
}
}
#endregion
}
II: Usage:
public static void Main()
{
Class c = new Class();
dynamic o = new ExpandoObject();
o.Name = "Carl";
o.Level = 7;
o.Inner = new InnerClass
{
Name = "Inner Carl",
Level = 10
};
Mapper<Class>.Map(o, c);
Console.Read();
}
internal class Class
{
public string Name { get; set; }
public int? Level { get; set; }
public InnerClass Inner { get; set; }
public string Property;
}
internal class InnerClass
{
public string Name { get; set; }
public int? Level { get; set; }
}
3- If the property is formated like this public string Property; the get properties does not get it.
Oh, that's not a property, that's a field. If you want consider fields as well.
static Mapper()
{
PropertyMap = typeof(T).GetProperties(BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance)
.ToDictionary(p => p.Name.ToLower(), p => p);
FieldMap = typeof(T).GetFields(BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance)
.ToDictionary(f => f.Name.ToLower(), f => f);
}
2- When i try to map int to a Nullable simply it will throw a type mismatch because i can't find a way to detect and cast it properly.
Why check for Nullable type, let reflection figure it out. If value is valid, it will be assigned.
public static void Map(ExpandoObject source, T destination)
{
if (source == null)
throw new ArgumentNullException("source");
if (destination == null)
throw new ArgumentNullException("destination");
foreach (var kv in source)
{
PropertyInfo p;
if (PropertyMap.TryGetValue(kv.Key.ToLower(), out p))
{
p.SetValue(destination, kv.Value, null);
}
else
{
FieldInfo f;
if (FieldMap.TryGetValue(kv.Key.ToLower(), out f))
{
f.SetValue(destination, kv.Value);
}
}
}
}
1 - It does not recursively map the inner objects of the ExpandoObject as supposed.
Seems to work for your InnerClass at least.
Class c = new Class();
dynamic o = new ExpandoObject();
o.Name = "Carl";
o.Level = 7;
o.Inner = new InnerClass
{
Name = "Inner Carl",
Level = 10
};
o.Property = "my Property value"; // dont forget to set this
Mapper<Class>.Map(o, c);
EDIT: based on your comments, I've create two overloaded methods MergeProperty. You can write similarly overloaded methods for fields.
public static void MergeProperty(PropertyInfo pi, ExpandoObject source, object target)
{
Type propType = pi.PropertyType;
// dont recurse for value type, Nullable<T> and strings
if (propType.IsValueType || propType == typeof(string))
{
var sourceVal = source.First(kvp => kvp.Key == pi.Name).Value;
if(sourceVal != null)
pi.SetValue(target, sourceVal, null);
}
else // recursively map inner class properties
{
var props = propType.GetProperties(BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance);
foreach (var p in props)
{
var sourcePropValue = source.First(kvp => kvp.Key == pi.Name).Value;
var targetPropValue = pi.GetValue(target, null);
if (sourcePropValue != null)
{
if (targetPropValue == null) // replace
{
pi.SetValue(target, source.First(kvp => kvp.Key == pi.Name).Value, null);
}
else
{
MergeProperty(p, sourcePropValue, targetPropValue);
}
}
}
}
}
public static void MergeProperty(PropertyInfo pi, object source, object target)
{
Type propType = pi.PropertyType;
PropertyInfo sourcePi = source.GetType().GetProperty(pi.Name);
// dont recurse for value type, Nullable<T> and strings
if (propType.IsValueType || propType == typeof(string))
{
var sourceVal = sourcePi.GetValue(source, null);
if(sourceVal != null)
pi.SetValue(target, sourceVal, null);
}
else // recursively map inner class properties
{
var props = propType.GetProperties(BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance);
foreach (var p in props)
{
var sourcePropValue = sourcePi.GetValue(source, null);
var targetPropValue = pi.GetValue(target, null);
if (sourcePropValue != null)
{
if (targetPropValue == null) // replace
{
pi.SetValue(target, sourcePi.GetValue(source, null), null);
}
else
{
MergeProperty(p, sourcePropValue, targetPropValue);
}
}
}
}
}
You can use the methods this way:
public static void Map(ExpandoObject source, T destination)
{
if (source == null)
throw new ArgumentNullException("source");
if (destination == null)
throw new ArgumentNullException("destination");
foreach (var kv in source)
{
PropertyInfo p;
if (PropertyMap.TryGetValue(kv.Key.ToLower(), out p))
{
MergeProperty(p, source, destination);
}
else
{
// do similar merge for fields
}
}
}
I have the following two classes:
public class Address
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
}
public class Employee
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public Address EmployeeAddress { get; set; }
}
I have an instance of the employee class as follows:
var emp1Address = new Address();
emp1Address.AddressLine1 = "Microsoft Corporation";
emp1Address.AddressLine2 = "One Microsoft Way";
emp1Address.City = "Redmond";
emp1Address.State = "WA";
emp1Address.Zip = "98052-6399";
var emp1 = new Employee();
emp1.FirstName = "Bill";
emp1.LastName = "Gates";
emp1.EmployeeAddress = emp1Address;
I have a method which gets the property value based on the property name as follows:
public object GetPropertyValue(object obj ,string propertyName)
{
var objType = obj.GetType();
var prop = objType.GetProperty(propertyName);
return prop.GetValue(obj, null);
}
The above method works fine for calls like GetPropertyValue(emp1, "FirstName") but if I try GetPropertyValue(emp1, "Address.AddressLine1") it throws an exception because objType.GetProperty(propertyName); is not able to locate the nested object property value. Is there a way to fix this?
public object GetPropertyValue(object obj, string propertyName)
{
foreach (var prop in propertyName.Split('.').Select(s => obj.GetType().GetProperty(s)))
obj = prop.GetValue(obj, null);
return obj;
}
Thanks, I came here looking for an answer to the same problem. I ended up modifying your original method to support nested properties. This should be more robust than having to do nested method calls which could end up being cumbersome for more than 2 nested levels.
This will work for unlimited number of nested property.
public object GetPropertyValue(object obj, string propertyName)
{
var _propertyNames = propertyName.Split('.');
for (var i = 0; i < _propertyNames.Length; i++)
{
if (obj != null)
{
var _propertyInfo = obj.GetType().GetProperty(_propertyNames[i]);
if (_propertyInfo != null)
obj = _propertyInfo.GetValue(obj);
else
obj = null;
}
}
return obj;
}
Usage:
GetPropertyValue(_employee, "Firstname");
GetPropertyValue(_employee, "Address.State");
GetPropertyValue(_employee, "Address.Country.Name");
var address = GetPropertyValue(GetPropertyValue(emp1, "Address"), "AddressLine1");
Object Employee doesn't have a single property named "Address.AddressLine1", it has a property named "Address", which itself has a property named "AddressLine1".
I use this method to get the values from properties (unlimited number of nested property) as below:
"Property"
"Address.Street"
"Address.Country.Name"
public static object GetPropertyValue(object src, string propName)
{
if (src == null) throw new ArgumentException("Value cannot be null.", "src");
if (propName == null) throw new ArgumentException("Value cannot be null.", "propName");
if(propName.Contains("."))//complex type nested
{
var temp = propName.Split(new char[] { '.' }, 2);
return GetPropertyValue(GetPropertyValue(src, temp[0]), temp[1]);
}
else
{
var prop = src.GetType().GetProperty(propName);
return prop != null ? prop.GetValue(src, null) : null;
}
}
Here the Fiddle: https://dotnetfiddle.net/PvKRH0
Yet another variation to throw out there. Short & sweet, supports arbitrarily deep properties, handles null values and invalid properties:
public static object GetPropertyVal(this object obj, string name) {
if (obj == null)
return null;
var parts = name.Split(new[] { '.' }, 2);
var prop = obj.GetType().GetProperty(parts[0]);
if (prop == null)
throw new ArgumentException($"{parts[0]} is not a property of {obj.GetType().FullName}.");
var val = prop.GetValue(obj);
return (parts.Length == 1) ? val : val.GetPropertyVal(parts[1]);
}
Get Nest properties e.g., Developer.Project.Name
private static System.Reflection.PropertyInfo GetProperty(object t, string PropertName)
{
if (t.GetType().GetProperties().Count(p => p.Name == PropertName.Split('.')[0]) == 0)
throw new ArgumentNullException(string.Format("Property {0}, is not exists in object {1}", PropertName, t.ToString()));
if (PropertName.Split('.').Length == 1)
return t.GetType().GetProperty(PropertName);
else
return GetProperty(t.GetType().GetProperty(PropertName.Split('.')[0]).GetValue(t, null), PropertName.Split('.')[1]);
}
I made an extension method on type for this propose:
public static class TypeExtensions
{
public static PropertyInfo GetSubProperty(this Type type, string treeProperty, object givenValue)
{
var properties = treeProperty.Split('.');
var value = givenValue;
foreach (var property in properties.Take(properties.Length - 1))
{
value = value.GetType().GetProperty(property).GetValue(value);
if (value == null)
{
return null;
}
}
return value.GetType().GetProperty(properties[properties.Length - 1]);
}
public static object GetSubPropertyValue(this Type type, string treeProperty, object givenValue)
{
var properties = treeProperty.Split('.');
return properties.Aggregate(givenValue, (current, property) => current.GetType().GetProperty(property).GetValue(current));
}
}
A Modified version of above to get the multilevel nested properties
private static System.Reflection.PropertyInfo GetProperty(object t, string PropertName, out object Value)
{
Value = "";
var v = t.GetType().GetProperties();
if (t.GetType().GetProperties().Count(p => p.Name == PropertName.Split('.')[0]) == 0)
//throw new ArgumentNullException(string.Format("Property {0}, is not exists in object {1}", PropertName, t.ToString()));
return null;
if (PropertName.Split('.').Length == 1)
{
var Value1 = t.GetType().GetProperty(PropertName).GetValue(t, null);
Value = Value1;//.ToString();
return t.GetType().GetProperty(PropertName);
}
else
{
//return GetProperty(t.GetType().GetProperty(PropertName.Split('.')[0]).GetValue(t, null), PropertName.Split('.')[1], out Value);
return GetProperty(t.GetType().GetProperty(PropertName.Split('.')[0]).GetValue(t, null), PropertName.Substring(PropertName.IndexOf('.') + 1, PropertName.Length - PropertName.IndexOf('.') - 1), out Value);
}
}
This will work for level 1 and level 2 object properties e.g. Firstname and Address.AddressLine1
public object GetPropertyValue(object obj, string propertyName)
{
object targetObject = obj;
string targetPropertyName = propertyName;
if (propertyName.Contains('.'))
{
string[] split = propertyName.Split('.');
targetObject = obj.GetType().GetProperty(split[0]).GetValue(obj, null);
targetPropertyName = split[1];
}
return targetObject.GetType().GetProperty(targetPropertyName).GetValue(targetObject, null);
}
I have a problem with struct type in static class, So I must use this method GetNestedType, this is example code if you know property name, If you want to getAll you can use GetNestedTypes
ExpandoObject in this example just use for dynamic add property and value
private void ExtractValuesFromAppconstants(string keyName)
{
Type type = typeof(YourClass);
var examination = type.GetNestedType(keyName);
if (examination != null)
{
var innerTypes = examination.GetNestedTypes();
foreach (var innerType in innerTypes)
{
Console.Writeline($"{innerType.Name}")
}
}
}
Recursive method, in one line...
object GetPropertyValue(object obj, string propertyName)
{
return propertyName.Contains(".") ? GetPropertyValue(obj.GetType().GetProperty(propertyName.Split(".").First()).GetValue(obj), string.Join(".", propertyName.Split(".").Skip(1))) : obj != null ? obj.GetType().GetProperty(propertyName).GetValue(obj) : null;
}
I found that the code posted by DevT almost did the trick but failed when there are collections involved e.g. Applicant.Addresses[0].FirstLine, so I added some code to fix this. I am sure others can improve upon in.
public static object GetPropertyValue(object src, string propName)
{
if (src == null) throw new ArgumentException("Value cannot be null.", "src");
if (propName == null) throw new ArgumentException("Value cannot be null.", "propName");
if (propName.Contains("."))//complex type nested
{
var temp = propName.Split(new char[] { '.' }, 2);
return GetPropertyValue(GetPropertyValue(src, temp[0]), temp[1]);
}
else
{
if (propName.Contains("["))
{
int iterator_start = propName.IndexOf('[');
int iterator_end = propName.IndexOf(']');
string iterator_value = propName.Substring(iterator_start + 1, iterator_end - iterator_start - 1);
string string_to_remove = "[" + iterator_value + "]";
int iterator_number = Convert.ToInt32(iterator_value);
propName = propName.Replace(string_to_remove, "");
var prop2 = src.GetType().GetProperty(propName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
Type type = prop2.PropertyType;
if (type.IsGenericType && type.GetGenericTypeDefinition()
== typeof(List<>))
{
System.Collections.IList oTheList = (System.Collections.IList)prop2.GetValue(src, null);
return oTheList[iterator_number];
}
}
var prop = src.GetType().GetProperty(propName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
return prop != null ? prop.GetValue(src, null) : null;
}
}