Sorry if this is a duplicate. I searched and was not able to find an answer.
How do I use reflection to get Values of Properties of class at multilevel?
I have a List of string that has some string values like this:
ClassNames = {"FirstName", "LastName", "Location.City", "Location.State", "Location.Country", "PhoneNo"}
I have two classes
public class Details
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Location Location { get; set; }
public string PhoneNo { get; set; }
}
public class Location
{
public long City { get; set; }
public string State { get; set; }
public string Country { get; set; }
}
I used reflection and I am able to get the values of firstname, lastname and phone. But how do I get the values in the location class? It throws an Error. I change the List to just have Location / City . I am missing something. I dont want to do Multiple for loops as the level might drill down to n level. (4 max to be realistic)
foreach (string fieldname in ClassNames)
{
string fieldvalue = RestultDTO[indexValue]GetType().GetProperty(fieldname) ;
}
You'd have to get the Location instance first:
var s = "Location.City";
var sVals = s.Split('.');
// here [t] is the TypeInfo of [Details]
var l = t.GetProperty(sVals[0]);
^ gets the Location PropertyInfo (really only need to do this once
var val = l.GetProperty(sVals[1]).GetValue(l.GetValue(o));
^ gets the PropertyInfo for the property you're after (City in this case)
^ gets the actual value of that property
^ gets the value of Location for o where o is an instance of Details
If you're using a version before 4.5 you'll probably need to send in one additional parameter to the GetValue method - it can be , null both times because the properties aren't indexers.
Here are two methods I wrote to solve this issue
This one gets a queue of properties from a base object:
private static Queue<PropertyInfo> GetProperty(object obj, string path)
{
Queue<PropertyInfo> values = new Queue<PropertyInfo>();
Type object_type = obj.GetType();
string[] properties = path.Split('.');
PropertyInfo propertyInfo = null;
foreach (string property in properties)
{
if (propertyInfo != null)
{
Type propertyType = propertyInfo.PropertyType;
propertyInfo = propertyType.GetProperty(property);
values.Enqueue(propertyInfo);
}
else
{
propertyInfo = object_type.GetProperty(property);
values.Enqueue(propertyInfo);
}
}
return values;
}
And this one uses the queue and the object to get the actual values:
private static T GetValue<T>(object obj, Queue<PropertyInfo> values)
{
object location = obj;
while (values.Count > 0)
{
location = values.Dequeue().GetValue(location);
}
return (T)location;
}
Probably wouldn't hurt to add some error checking and such as well.
Related
I used to get one name for one property, but now I get data with two names for one property. That is, it was ServiceName ->ServiceName, and it became ServiceName ->ServiceName, Name.
value assignment code:
PropertyInfo property = item.GetType().GetProperty(column.Caption);
var range = worksheet.Cell(i, listIndex[indexForList]);
if (range == null)
{
continue;
}
var value = ChangeType((object)range.Value, property.PropertyType);
property.SetValue(item, value);
I have a model:
public class ImportAdditionalService
{
public string ServiceName { get; set; }
public decimal? Price { get; set; }
}
Are there any attributes in this case for class properties, so that I can use reflection for two names?
I´ve been searching and trying for some hours now but it doesn´t make sense to me right now.
Basically I have two class libraries in a .net 4.5 project.
On using reflection I´m able to access some properties but not all and I d not see my fault.
Library A defines the datastructure:
public class HeaderRow
{
public string Format { get; set; }
public Int32 Version { get; set; } = 0;
public CustomEnum Datacategory { get; set; }
public Custom2Enum FormatnameEnum { get; set; }
public int Formatversion { get; set; }
public UInt64 CreatedAt { get; set; }
public string Origin {get; set;}
}
which I´d like to fill in library 2 with this code:
protected PropertyInfo FindPropertyInfo(object o, string propertyName)
{
Type objektTyp = o.GetType();
PropertyInfo info = objektTyp.GetProperty(propertyName,BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
return info;
}
public override void FillValueToColumns(object o, string property, string value)
{
FindPropertyInfo(o,property).SetValue(o,value);
}
I have access to:
Format but not Origin
CreatedAt but not Formatversion
and I do not see my custom Enums.
For all those I do not see "info" is null.
Any help is apreciated.
Thanks
You will not be able to use SetValue to assign a string to, for example, an int. To do that you will either need overloads of FillValueToColumns that take the int (for example) as the last parameter, or else you need to get a type converter, like this:
PropertyInfo propertyInfo = FindPropertyInfo( o, propertyName);
TypeConverter typeConverter = TypeDescriptor.GetConverter( propertyInfo.PropertyType);
object v = typeConverter.ConvertFrom( null, CultureInfo.CurrentCulture, value ?? "");
if (v == null && propertyInfo.PropertyType.IsValueType && Nullable.GetUnderlyingType(propertyInfo.PropertyType) == null) {
throw new InvalidCastException("Assignment of null to a non-nullable value type!");
}
propertyInfo.SetValue( object, v);
sjb
Kudos to you. My bad. Sorry.
Indeed a naming issue.
By copy & paste I created blanks in my string properties. So I was searching for
"Origin "
and not
"Origin"
-> Deleting surplus blanks solved the issue.
How do I do a mapping between an object created by instantiating a class and a dynamic object created by a JSON deserialization (JsonConvert) considering I don't know the dynamic object's fields? In other words, I'd like to update the dynamic object fields matching by name.
This is my example code:
string json = {\"NDG\":7803, \"NumberOfNights\":2, \"Nome\":\"Ago\", \"Cognome\":\"Mar\", \"CognomeNome\":\"\"};
string djson = ?? //I don't know the structure coming from a call as parameter but I know there are some json string identical fields
public class myVars
{
public string Userid { get; set; }
public string Nome { get; set; }
public string Cognome { get; set; }
public string CognomeNome { get; set; }
}
myVars object1 = JsonConvert.DeserializeObject<myVars>(json);
dynamic object2 = JObject.Parse(djson); // object2 contains a field named "CognomeNome"
myVars.CognomeNome = myVars.Cognome + myVarsNome;
MapObjects(object1 , object2);
string rjson = JsonConvert.SerializeObject(object2); //returns {"CognomeNome":""}
public static object MapObjects(object source, object target)
{
foreach (PropertyInfo sourceProp in source.GetType().GetProperties())
{
PropertyInfo targetProp = target.GetType().GetProperties().Where(p => p.Name == sourceProp.Name).FirstOrDefault();
if (targetProp != null && targetProp.GetType().Name == sourceProp.GetType().Name)
{
targetProp.SetValue(target, sourceProp.GetValue(source));
}
}
return target;
}
The simplest way is to use one of the existing automappers (Alternatives to AutoMapper)
For example, this:
https://github.com/agileobjects/AgileMapper/wiki/Performing-Updates sounds like it's what you are asking for.
I am having trouble getting the property Names of the IEnumerable properties in my models. I cant seem to get the Nested IEnumerables from the TModel classes. I have looked into some reflection examples but haven't something quite along these lines.
I am looking to just get the IEnumerable property names for each nested model and send the property name to a list. The actual value is not important.
Any help would be greatly appreciated.
// TModel = DataContent in this context.
public class GetModelBase<TModel>
{
public string Error { get; set; }
public IEnumerable<TModel> DataContent { get; set; }
}
public class DataContent
{
public int Total { get; set; }
public IEnumerable<Data> Data { get; set; }
}
public class Data
{
public int DataId{ get; set; }
IEnumerable<DataInformation> DataInformation{ get; set; }
}
public IEnumerable<GetModelBase<TModel>> ResponseAsList<TModel>()
{
// ResponseBody in this context is a string representation of json of the models above...
var toArray = new ConvertJsonArray<GetModelBase<TModel>>(ResponseBody).ReturnJsonArray();
}
// T = GetModelBase<DataContent> in this context.
public class ConvertJsonArray<T>
{
public ConvertJsonArray(string responseString)
{
_responseString = responseString;
Convert();
}
public void Convert()
{
var result = JObject.Parse(_responseString);
// This is where I am having trouble... I am unable to get the nested IEnumerable names.
Type t = typeof(T);
PropertyInfo[] propertyInformation = t.GetProperties(BindingFlags.Public|BindingFlags.Instance);
List<string> toLists = new List<string>();
foreach (PropertyInfo pi in propertyInformation)
toLists.Add(pi.Name);
// End of Property Information Issuse...
foreach (string s in toLists.ToArray())
{
if (result[s] != null)
{
if (!(result[s] is JArray)) result[s] = new JArray(result[s]);
}
}
_jsonAsArray = result.ToString();
}
public string ReturnJsonArray()
{
return _jsonAsArray;
}
private string _responseString { get; set; }
private string _jsonAsArray { get; set; }
}
The result I am looking for in the above code sample would be a list containing only the IEnumerable names as such { "DataContent", "Data", "DataInformation" }
UPDATE:
I am still having trouble looping through each model. I have a nearly working code example.
// This replaces the Type code in the Convert method...
GetProperties(typeof(T))
private void GetProperties(Type classType)
{
foreach (PropertyInfo property in classType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (property.PropertyType.IsGenericType && (property.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
{
ValuesToList.Add(property.Name);
foreach (Type nestedType in property.PropertyType.GetGenericArguments())
{
GetProperties(nestedType);
}
}
}
}
private List<string> ValuesToList { get; set; }
The results for this yields { "DataContent", "Data" } but fails to get "DataInformation". For some reason the IEnumerables are not hit while in the foreach loop. Additional help would be appreciated.
You already have the PropertyInfo, so you are almost there - all that is left is to recognize which properties are of type IEnumerable<...>, where ... can be an arbitrary type.
For this purpose, check the PropertyType property.
It is a Type instance for which you can check whether it is based upon the generic type definition IEnumerable<T> by means of the GetGenericTypeDefinition method.
That method will throw an exception for non-generic types, so you will also have to check IsGenericType:
if (pi.PropertyType.IsGenericType
&& (pi.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
{
toLists.Add(pi.Name);
}
Take this class for example:
public class Applicant : UniClass<Applicant>
{
[Key]
public int Id { get; set; }
[Field("X.838.APP.SSN")]
public string SSN { get; set; }
[Field("APP.SORT.LAST.NAME")]
public string FirstName { get; set; }
[Field("APP.SORT.FIRST.NAME")]
public string LastName { get; set; }
[Field("X.838.APP.MOST.RECENT.APPL")]
public int MostRecentApplicationId { get; set; }
}
How would I go about getting all of the properties that are decorated with the field attribute, get their types, and then assign a value to them?
This is all done with reflection. Once you have a Type object, you can then get its PropertyInfo with myType.GetProperties(), from there, you can get each property's attributes with GetCustomAttributes(), and from there if you find your attribute, you've got a winner, and then you can proceed to work with it as you please.
You already have the PropertyInfo object, so you can assign to it with PropertyInfo.SetValue(object target, object value, object[] index)
You'll need to use Reflection:
var props =
from prop in typeof(Applicant).GetProperties()
select new {
Property = prop,
Attrs = prop.GetCustomAttributes(typeof(FieldAttribute), false).Cast<FieldAttribute>()
} into propAndAttr
where propAndAttr.Attrs.Any()
select propAndAttr;
You can then iterate through this query to set the values:
foreach (var prop in props) {
var propType = prop.Property.PropertyType;
var valueToSet = GetAValueToSet(); // here's where you do whatever you need to do to determine the value that gets set
prop.Property.SetValue(applicantInstance, valueToSet, null);
}
You would just need to invoke the appropriate reflection methods - try this:
<MyApplicationInstance>.GetType().GetProperties().Where(x => x.GetCustomAttributes().Where(y => (y as FieldAttribute) != null).Count() > 0);