I found that code of #RichTebb is great and it returns the Model attribute DisplayName.
But how to iterate through the all Model Display(Name=) attribute values then?
Thanks for ANY clue!
#RichTebb code
public static class HelperReflectionExtensions
{
public static string GetPropertyDisplayString<T>(Expression<Func<T, object>> propertyExpression)
{
var memberInfo = GetPropertyInformation(propertyExpression.Body);
if (memberInfo == null)
{
throw new ArgumentException(
"No property reference expression was found.",
"propertyExpression");
}
var displayAttribute = memberInfo.GetAttribute<DisplayAttribute>(false);
if (displayAttribute != null)
{
return displayAttribute.Name;
}
// ReSharper disable RedundantIfElseBlock
else
// ReSharper restore RedundantIfElseBlock
{
var displayNameAttribute = memberInfo.GetAttribute<DisplayNameAttribute>(false);
if (displayNameAttribute != null)
{
return displayNameAttribute.DisplayName;
}
// ReSharper disable RedundantIfElseBlock
else
// ReSharper restore RedundantIfElseBlock
{
return memberInfo.Name;
}
}
}
public static MemberInfo GetPropertyInformation(Expression propertyExpression)
{
Debug.Assert(propertyExpression != null, "propertyExpression != null");
var memberExpr = propertyExpression as MemberExpression;
if (memberExpr == null)
{
var unaryExpr = propertyExpression as UnaryExpression;
if (unaryExpr != null && unaryExpr.NodeType == ExpressionType.Convert)
{
memberExpr = unaryExpr.Operand as MemberExpression;
}
}
if (memberExpr != null && memberExpr.Member.MemberType == MemberTypes.Property)
{
return memberExpr.Member;
}
return null;
}
public static T GetAttribute<T>(this MemberInfo member, bool isRequired)
where T : Attribute
{
var attribute = member.GetCustomAttributes(typeof(T), false).SingleOrDefault();
if (attribute == null && isRequired)
{
throw new ArgumentException(
string.Format(
CultureInfo.InvariantCulture,
"The {0} attribute must be defined on member {1}",
typeof(T).Name,
member.Name));
}
return (T)attribute;
}
}
Sample:
string displayName = ReflectionExtensions.GetPropertyDisplayName<SomeClass>(i => i.SomeProperty);
3 hours and I found the solution.
First of all
[Display(Name = "Employed: ")]
public Nullable<bool> Employed { get; set; }
and
[DisplayName("Employed: ")]
public Nullable<bool> Employed { get; set; }
are not the same. :) For MVC we have to use this syntax [DisplayName("Employed: ")]
Also the class metadata attribute should look like
[MetadataType(typeof(PatientMetadata))]
public partial class Patient
{
....
internal sealed class PatientMetadata
{
And finally the CODE
public static class DisplayNameHelper
{
public static string GetDisplayName(object obj, string propertyName)
{
if (obj == null) return null;
return GetDisplayName(obj.GetType(), propertyName);
}
public static string GetDisplayName(Type type, string propertyName)
{
var property = type.GetProperty(propertyName);
if (property == null) return null;
return GetDisplayName(property);
}
public static string GetDisplayName(PropertyInfo property)
{
var attrName = GetAttributeDisplayName(property);
if (!string.IsNullOrEmpty(attrName))
return attrName;
var metaName = GetMetaDisplayName(property);
if (!string.IsNullOrEmpty(metaName))
return metaName;
return property.Name.ToString(CultureInfo.InvariantCulture);
}
private static string GetAttributeDisplayName(PropertyInfo property)
{
var atts = property.GetCustomAttributes(
typeof(DisplayNameAttribute), true);
if (atts.Length == 0)
return null;
var displayNameAttribute = atts[0] as DisplayNameAttribute;
return displayNameAttribute != null ? displayNameAttribute.DisplayName : null;
}
private static string GetMetaDisplayName(PropertyInfo property)
{
if (property.DeclaringType != null)
{
var atts = property.DeclaringType.GetCustomAttributes(
typeof(MetadataTypeAttribute), true);
if (atts.Length == 0)
return null;
var metaAttr = atts[0] as MetadataTypeAttribute;
if (metaAttr != null)
{
var metaProperty =
metaAttr.MetadataClassType.GetProperty(property.Name);
return metaProperty == null ? null : GetAttributeDisplayName(metaProperty);
}
}
return null;
}
}
How to use:
var t = patient.GetType();
foreach (var pi in t.GetProperties())
{
var dn = DisplayNameHelper.GetDisplayName(pi);
}
DONE!!!!
Type t = model.GetType();
foreach (PropertyInfo pi in t.GetProperties())
{
var attr = pi.GetCustomAttribute(DisplayNameAttribute, true);
if (attr != null) ...
}
Related
I want to implement batch delete (for performance reasons) in Entity framework like this:
context.ExecuteStoreCommand("DELETE FROM {0} WHERE {1} = {2}", tableName, columnName, columnValue);
I want to know how to get the column's name from the property.
[Column("ColumnName")]
public int PropertyName { get; set; }
I Also use EF5 with Oracle provider.
You use Reflection.
public class MyClass
{
[Column("ColumnName")]
public int PropertyName { get; set; }
}
Using Reflection:
public static string GetColumnName<T>(string propertyName)
{
string result = null;
if (string.IsNullOrWhiteSpace(propertyName))
{
throw new ArgumentNullException();
}
var tableType = typeof(T);
var properties = tableType.GetProperties(BindingFlags.GetProperty
| BindingFlags.Instance
| BindingFlags.Public);
var property = properties
.Where(p => p.Name.Equals(propertyName,
StringComparison.CurrentCultureIgnoreCase))
.FirstOrDefault();
if (property == null)
{
throw new Exception("PropertyName not found."); // bad example
}
else
{
result = property.Name; //if no column attribute exists;
var attributes = property.GetCustomAttributes();
var attribute = attributes
.Where(a => a is ColumnAttribute)
.FirstOrDefault() as ColumnAttribute;
if (attribute != null)
{
result = attribute.Name;
}
}
return result;
}
For example:
public class MyClass
{
[Column("ColumnName")]
public int PropertyName { get; set; }
}
var result = GetColumnName<MyClass>("PropertyName");
Console.WriteLine(result);
Result:
ColumnName
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
}
}
}
the following function needs to look inside the input object if there is a property in it that returns a custom object it needs to do the trimming of that object as well. The code below works for the input object fine, but wont recursively look into a property that returns a custom object and do the trimming process.
public object TrimObjectValues(object instance)
{
var props = instance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
// Ignore non-string properties
.Where(prop => prop.PropertyType == typeof(string) | prop.PropertyType == typeof(object))
// Ignore indexers
.Where(prop => prop.GetIndexParameters().Length == 0)
// Must be both readable and writable
.Where(prop => prop.CanWrite && prop.CanRead);
foreach (PropertyInfo prop in props)
{
if (prop.PropertyType == typeof(string))
{
string value = (string)prop.GetValue(instance, null);
if (value != null)
{
value = value.Trim();
prop.SetValue(instance, value, null);
}
}
else if (prop.PropertyType == typeof(object))
{
TrimObjectValues(prop);
}
}
return instance;
}
I need to change this somehow to look for other objects inside the initial object
.Where(prop => prop.PropertyType == typeof(string) | prop.PropertyType == typeof(object))
This code isn't working reason is for a example is the object I am passing as input has a property that returns a type of "Address" therefore typeof(object) never gets hit.
Here is a tree of data to test against pass the function "o" in this case
Order o = new Order();
o.OrderUniqueIdentifier = "TYBDEU83e4e4Ywow";
o.VendorName = "Kwhatever";
o.SoldToCustomerID = "Abc98971";
o.OrderType = OrderType.OnOrBefore;
o.CustomerPurchaseOrderNumber = "MOOMOO 56384";
o.EmailAddress = "abc#electric.com";
o.DeliveryDate = DateTime.Now.AddDays(35);
Address address1 = new Address();
//address1.AddressID = "Z0mmn01034";
address1.AddressID = "E0000bbb6 ";
address1.OrganizationName = " Nicks Organization ";
address1.AddressLine1 = " 143 E. WASHINGTON STREET ";
address1.City = " Rock ";
address1.State = "MA ";
address1.ZipCode = " 61114";
address1.Country = "US ";
o.ShipToAddress = address1;
Your tests with typeof(object) will all fail.
Try like this:
static void TrimObjectValues(object instance)
{
// if the instance is null we have nothing to do here
if (instance == null)
{
return;
}
var props = instance
.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
// Ignore indexers
.Where(prop => prop.GetIndexParameters().Length == 0)
// Must be both readable and writable
.Where(prop => prop.CanWrite && prop.CanRead);
foreach (PropertyInfo prop in props)
{
if (prop.PropertyType == typeof(string))
{
// if we have a string property we trim it
string value = (string)prop.GetValue(instance, null);
if (value != null)
{
value = value.Trim();
prop.SetValue(instance, value, null);
}
}
else
{
// if we don't have a string property we recurse
TrimObjectValues(prop.GetValue(instance, null));
}
}
}
I have also made the function return no value because you are modifying the argument instance anyway.
Test case:
public enum OrderType
{
OnOrBefore
}
public class Order
{
public string OrderUniqueIdentifier { get; set; }
public string VendorName { get; set; }
public string SoldToCustomerID { get; set; }
public OrderType OrderType { get; set; }
public string CustomerPurchaseOrderNumber { get; set; }
public string EmailAddress { get; set; }
public DateTime DeliveryDate { get; set; }
public Address ShipToAddress { get; set; }
}
public class Address
{
public string AddressID { get; set; }
public string OrganizationName { get; set; }
public string AddressLine1 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
public string Country { get; set; }
}
class Program
{
static void Main()
{
Order o = new Order();
o.OrderUniqueIdentifier = "TYBDEU83e4e4Ywow";
o.VendorName = "Kwhatever";
o.SoldToCustomerID = "Abc98971";
o.OrderType = OrderType.OnOrBefore;
o.CustomerPurchaseOrderNumber = "MOOMOO 56384";
o.EmailAddress = "abc#electric.com";
o.DeliveryDate = DateTime.Now.AddDays(35);
Address address1 = new Address();
//address1.AddressID = "Z0mmn01034";
address1.AddressID = "E0000bbb6 ";
address1.OrganizationName = " Nicks Organization ";
address1.AddressLine1 = " 143 E. WASHINGTON STREET ";
address1.City = " Rock ";
address1.State = "MA ";
address1.ZipCode = " 61114";
address1.Country = "US ";
o.ShipToAddress = address1;
TrimObjectValues(o);
}
static void TrimObjectValues(object instance)
{
if (instance == null)
{
return;
}
var props = instance
.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
// Ignore indexers
.Where(prop => prop.GetIndexParameters().Length == 0)
// Must be both readable and writable
.Where(prop => prop.CanWrite && prop.CanRead);
foreach (PropertyInfo prop in props)
{
if (prop.PropertyType == typeof(string))
{
string value = (string)prop.GetValue(instance, null);
if (value != null)
{
value = value.Trim();
prop.SetValue(instance, value, null);
}
}
else
{
TrimObjectValues(prop.GetValue(instance, null));
}
}
}
}
UPDATE 2:
It seems that you want to handle also lists of objects. You could adapt the method:
static void TrimObjectValues(object instance)
{
if (instance == null)
{
return;
}
var props = instance
.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
// Ignore indexers
.Where(prop => prop.GetIndexParameters().Length == 0)
// Must be both readable and writable
.Where(prop => prop.CanWrite && prop.CanRead);
if (instance is IEnumerable)
{
foreach (var element in (IEnumerable)instance)
{
TrimObjectValues(element);
}
return;
}
foreach (PropertyInfo prop in props)
{
if (prop.PropertyType == typeof(string))
{
string value = (string)prop.GetValue(instance, null);
if (value != null)
{
value = value.Trim();
prop.SetValue(instance, value, null);
}
}
else
{
TrimObjectValues(prop.GetValue(instance, null));
}
}
}
prop.PropertyType == typeof(object) does not work, because this will only be true for object and not for derived types. You would have to write typeof(object).IsAssignableFrom(prop.PropertyType); however, this applies for all the types! Drop both conditions (for string and for object).
Note: Also drop the condition before the TrimObjectValues(prop);. (Replace else if (...) by else)
public object TrimObjectValues(object instance)
{
if (instance is string)
{
instance = ((string)instance).Trim();
return instance;
}
if (instance is IEnumerable)
{
foreach (var element in (IEnumerable)instance)
{
TrimObjectValues(element);
}
return instance;
}
var props = instance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
// Ignore non-string properties
.Where(prop => prop.PropertyType == typeof(string) | prop.PropertyType is object)
// Ignore indexers
.Where(prop => prop.GetIndexParameters().Length == 0)
// Must be both readable and writable
.Where(prop => prop.CanWrite && prop.CanRead);
foreach (PropertyInfo prop in props)
{
if (prop.PropertyType == typeof(string))
{
string value = (string)prop.GetValue(instance, null);
if (value != null)
{
value = value.Trim();
prop.SetValue(instance, value, null);
}
}
else if (prop.PropertyType is object)
{
TrimObjectValues(prop.GetValue(instance, null));
}
}
return instance;
}
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;
}
}
How do I add a attribute to a XmlArray element ( not to XmlArrayItem ) while serializing the object?
XmlArray is used to tell the xmlserializer to treat the property as array and serialize it according its parameters for the element names.
[XmlArray("FullNames")]
[XmlArrayItem("Name")]
public string[] Names{get;set;}
will give you
<FullNames>
<Name>Michael Jackson</Name>
<Name>Paris Hilton</Name>
</FullNames>
In order to add an xml attribute to FullNames element, you need declare a class for it.
[XmlType("FullNames")]
public class Names
{
[XmlAttribute("total")]
public int Total {get;set;}
[XmlElement("Name")]
public string[] Names{get;set;}
}
This will give you
<FullNames total="2">
<Name>Michael Jackson</Name>
<Name>Paris Hilton</Name>
</FullNames>
This can be done by deriving from IXmlSerializable. I have attached a sample with a base class which does the job:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace XmlSerializerApp {
class Program {
static void Main() {
using (var ms = new MemoryStream()) {
var serializer = new XmlSerializer(typeof(RootObject));
serializer.Serialize(ms, new RootObject());
ms.Position = 0;
var formatted =
new XmlDocument {
XmlResolver = null,
PreserveWhitespace = false
};
formatted.Load(ms);
var stringWriter = new StringWriter();
var xmlTextWriter =
new XmlTextWriter(stringWriter) {Formatting = Formatting.Indented};
formatted.WriteTo(xmlTextWriter);
Console.WriteLine(stringWriter.ToString());
ms.Position = 0;
var rootObj = serializer.Deserialize(ms) as RootObject;
if (rootObj?.Children != null) {
Console.WriteLine($"Whatever: {rootObj?.Children?.Whatever}");
foreach (var child in rootObj.Children) {
if (child == null) {
continue;
}
Console.WriteLine($" {child.Name}={child.Value}");
}
}
}
}
}
[XmlRoot(ElementName = "root")]
public class RootObject{
[XmlAttribute(AttributeName = "version")]
public string Version {get; set;} = "1.0.0";
[XmlElement(ElementName = "children")]
public ListOfChildren Children {get; set;} = new ListOfChildren {
new Child{ Name = "one", Value = "firstValue"}
};
}
[XmlRoot(ElementName = "add")]
public class Child {
[XmlAttribute(AttributeName = "name")]
public string Name { get; set; }
[XmlAttribute(AttributeName = "value")]
public string Value { get; set; }
}
public class ListOfChildren : ListBase<Child> {
[XmlAttribute(AttributeName = "whatever")]
public bool Whatever { get; set; } = true;
}
public class ListBase<T>
: List<T>
, IXmlSerializable
{
private static readonly Type _s_type = typeof(T);
// ReSharper disable once StaticMemberInGenericType
private static readonly XmlAttributeOverrides _s_overrides =
new Func<XmlAttributeOverrides>(
() => {
var overrides = new XmlAttributeOverrides();
overrides.Add(_s_type, new XmlAttributes{ XmlRoot = new XmlRootAttribute("add")});
return overrides;
})();
// ReSharper disable once StaticMemberInGenericType
private static readonly XmlSerializer _s_serializer = new XmlSerializer(_s_type, _s_overrides);
/// <inheritdoc />
public XmlSchema GetSchema() { throw new NotImplementedException(); }
/// <inheritdoc />
public void ReadXml(XmlReader reader) {
var localName = reader.LocalName;
var prefix = reader.Prefix;
var namespaceUri = reader.NamespaceURI;
var depth = reader.Depth;
var attributes =
GetAttributes()?.ToArray()
?? Array.Empty<KeyValuePair<PropertyInfo, XmlAttributeAttribute>>();
while (reader.MoveToNextAttribute()) {
var attribute =
attributes
.Where(
a =>
string.Equals(
a.Value?.AttributeName,
reader.LocalName,
StringComparison.Ordinal)
&& string.Equals(
a.Value?.Namespace ?? string.Empty,
reader.NamespaceURI,
StringComparison.Ordinal)
)
.Select(x => x.Key)
.FirstOrDefault();
if (attribute != null) {
var attributeValue = reader.Value;
if (attribute.PropertyType == typeof(string)) {
attribute.SetValue(attributeValue, null);
}
else if (attribute.PropertyType == typeof(bool)) {
if ("1".Equals(attributeValue, StringComparison.Ordinal)
|| "-1".Equals(attributeValue, StringComparison.Ordinal)
|| "TRUE".Equals(attributeValue, StringComparison.OrdinalIgnoreCase)) {
attribute.SetValue(this, true);
}
}
else if (attribute.PropertyType == typeof(short)
&& short.TryParse(attributeValue, out var shortValue)) {
attribute.SetValue(this, shortValue);
}
else if (attribute.PropertyType == typeof(int)
&& int.TryParse(attributeValue, out var intValue)) {
attribute.SetValue(this, intValue);
}
else if (attribute.PropertyType == typeof(long)
&& long.TryParse(attributeValue, out var longValue)) {
attribute.SetValue(this, longValue);
}
else if (attribute.PropertyType == typeof(decimal)
&& decimal.TryParse(attributeValue, out var decimalValue)) {
attribute.SetValue(this, decimalValue);
}
else if (attribute.PropertyType == typeof(float)
&& float.TryParse(attributeValue, out var floatValue)) {
attribute.SetValue(this, floatValue);
}
else if (attribute.PropertyType == typeof(double)
&& double.TryParse(attributeValue, out var doubleValue)) {
attribute.SetValue(this, doubleValue);
}
else if (attribute.PropertyType == typeof(Guid)
&& Guid.TryParse(attributeValue, out var guidValue)) {
attribute.SetValue(this, guidValue);
}
else if (attribute.PropertyType == typeof(Version)
&& Version.TryParse(attributeValue, out var versionValue)) {
attribute.SetValue(this, versionValue);
}
else if (attribute.PropertyType == typeof(Uri)
&& Uri.TryCreate(
attributeValue,
UriKind.RelativeOrAbsolute,
out var uriValue)) {
attribute.SetValue(this, uriValue);
}
}
}
Clear();
while (reader.Read()) {
if (reader.NodeType != XmlNodeType.Element) {
if (reader.NodeType == XmlNodeType.EndElement
&& prefix.Equals(reader.Prefix, StringComparison.Ordinal)
&& localName.Equals(reader.LocalName, StringComparison.Ordinal)
&& namespaceUri.Equals(reader.NamespaceURI, StringComparison.Ordinal)
&& depth == reader.Depth
) {
break;
}
continue;
}
var x = reader.ReadSubtree();
var item = (T)_s_serializer?.Deserialize(x);
Add(item);
}
}
/// <inheritdoc />
public void WriteXml(XmlWriter writer) {
var enumerable = GetAttributes();
if (enumerable != null) {
foreach (var attribute in enumerable) {
if (attribute.Key == null || attribute.Value?.AttributeName == null) {
continue;
}
var value = attribute.Key.GetValue(this, null);
if (value is bool b) {
value = b
? "true"
: "false";
}
if (value != null) {
writer.WriteAttributeString(attribute.Value.AttributeName,
attribute.Value.Namespace,
value.ToString() ?? string.Empty
);
}
}
}
foreach (var item in this) {
if (item == null) {
continue;
}
_s_serializer?.Serialize(writer, item);
}
}
private IEnumerable<KeyValuePair<PropertyInfo, XmlAttributeAttribute>> GetAttributes() {
return GetType()
.GetProperties()
.Select(
p =>
new KeyValuePair<PropertyInfo, XmlAttributeAttribute>(
p,
p.GetCustomAttributes(
typeof(XmlAttributeAttribute),
true)
.Cast<XmlAttributeAttribute>()
.FirstOrDefault())
)
.Where(x => x.Value != null);
}
}
}