Get Values From Complex Class Using Reflection - c#

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
}
}
}

Related

Preexisting Serializer to [FromQuery] format

I want to consume an ASP.NET Core Web API method that includes [FromQuery] parameters.
Since the format is somewhat unusual, I figured there would exist a library function that would take a complex type and generate the query string formatted text - however, I can't find one.
IOW, given a controller method defined like this:
[HttpGet("testing")]
public bool Testing([FromQuery]X x)
{
return (x?.Ys[1]?.Zs[1]?.Bs[3] == 3 && x?.Ys[1]?.Zs[0]?.A == 4);
}
And an X defined like this:
public class X
{
public Y[] Ys { get; set; }
}
public class Y
{
public Z[] Zs { get; set; }
}
public class Z
{
public int A { get; set; }
public int[] Bs { get; set; }
}
First of all, what's an example of what ASP.NET [FromQuery] is expecting to encounter in the query string in order to return true?
Secondly, is there a function somewhere that can serialize an object appropriately into whatever ASP.NET is expecting, or do I need to write one?
You can use the following "serializer"
public class QueryStringSerializer
{
private static bool IsPrimitive(object obj)
{
return obj.GetType().IsPrimitive || obj is string || obj is Guid;
}
private static bool IsEnumerable(object obj)
{
return obj is IEnumerable && !IsPrimitive(obj);
}
private static bool IsComplex(object obj)
{
return !(obj is IEnumerable) && !IsPrimitive(obj);
}
private static StringBuilder ToQueryStringInternal(object obj, string prop = null)
{
StringBuilder queryString = new StringBuilder();
//skip null values
if (obj == null)
return queryString;
Type type = obj.GetType();
PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
if (IsEnumerable(obj))
{
int i = 0;
foreach (object item in obj as IEnumerable)
{
string query = ToQueryStringInternal(item, $"{prop}[{i}]").ToString();
queryString.Append(query);
i++;
}
}
else if (IsComplex(obj))
{
foreach (PropertyInfo property in properties)
{
string propName = property.Name;
object value = property.GetValue(obj);
string name = prop == null ? propName : $"{prop}.{propName}";
string query = ToQueryStringInternal(value, name).ToString();
queryString.Append(query);
}
}
else
{
string encoded = HttpUtility.UrlEncode(Convert.ToString(obj));
queryString.Append($"{prop}={encoded}&");
}
return queryString;
}
public static string ToQueryString(object obj, string propertyName = null)
{
StringBuilder queryString = ToQueryStringInternal(obj, propertyName);
queryString.Length--;
return queryString.ToString();
}
}
Usage
var x = new X
{
Ys = new Y[] {
new Y {
Zs = new Z[] { new Z { } }
},
new Y {
Zs = new Z[] {
new Z { },
new Z {
A = 1,
Bs = new int[] { 0, 1, 2, 3 }
}
}
}
}
};
string query = QueryStringSerializer.ToQueryString(x);
Result
Ys[0].Zs[0].A=0&Ys[1].Zs[0].A=0&Ys[1].Zs[1].A=1&Ys[1].Zs[1].Bs[0]=0&Ys[1].Zs[1].Bs[1]=1&Ys[1].Zs[1].Bs[2]=2&Ys[1].Zs[1].Bs[3]=3
Caution
The serializer may still contain various bags. Also, do not create array with "gaps" such as
var q = new X[] {
new X { },
null, //a gap
new X { }
};
The result will be technically correct but ASP.NET model binder will bind only the first element properly.

Insert values in class without explicit writing them

I have some huge classes and don't want to write them all out for testing, because it's a huge effort and I could forget some values what makes the test invalid.
Messages = new List<Request.Notif.NotifRuleMessages>
{
new Request.Notif.NotifRuleMessages
{
Code = 1234,
Message = new List<Request.Notif.NotifRuleMessagesMessage>
{
new Request.Notif.NotifMessagesMessage
{
Status = new Request.Notif.NotifMessagesMessageStatus
{
Code = 1,
Bool = true,
Test1 = "Test",
Test2 = "Test"
},
Rules = new List<Request.Notif.NotifMessagesMessageRule>
{
new Request.Notif.NotifMessagesMessageRule
{
Lengths = new Request.Notif.NotifMessagesMessageRuleLength
{
Lenght = 1,
Lengths = new List<Request.Notif.NotifMessagesMessageRuleLengthLength>
{
new Request.Notif.NotifMessagesMessageRuleLengthLength
{
Type = "Test",
Value = 1
}
}
},
Status = new List<Request.Notif.NotifMessagesMessageRuleStatus>
{
new Request.Notif.NotifMessagesMessageRuleStatus
{
Test1 = "Test",
Test2 = "Test"
Is there a way to automaticly fill all int values with 1 or 0 and all string values with Test and especially all objects with the right class without unit testing and external libs?
Using reflection you could populate your objects recursively and set whatever default values you choose. A small example of a helper function that could do that for you:
void SetDefaults(object testObj)
{
var props = testObj.GetType().GetProperties();
foreach (var prop in props)
{
if (prop.GetSetMethod() == null)
{
continue;
}
var propType = prop.PropertyType;
if (propType == typeof(int))
{
prop.SetValue(testObj, 1);
}
else if (propType == typeof(bool))
{
prop.SetValue(testObj, false);
}
// More conditions...
else
{
var ctor = propType.GetConstructor(Type.EmptyTypes);
var propertyObject = ctor.Invoke(new object[0]);
SetDefaults(propertyObject);
prop.SetValue(testObj, propertyObject);
}
}
}
As you can see, if your tree of objects use types that don't have default constructors (parameterless constructors) you need some more complicated logic in the else-condition. Basically the stuff going on here is a very simplified version of what happens in a dependency injection framework.
To use it, do something like:
void Main()
{
TestObject obj = new TestObject();
SetDefaults(obj);
Console.WriteLine(obj);
}
class TestObject {
public int MyInt { get; set; }
public SubTestObject SubObj { get; set; }
}
class SubTestObject {
public int MyOwnInt { get; set; }
public bool MyBoolGetter => 1 > 0;
}

Reflection to get List<object> data

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.

How to correctly use Expression Trees on properties (without reflection)

Objective: process an object and if the object implements an expected type, I want to change a specific property value (this part is working fine), and I also would like to apply the same logic to all property lists (that I explicit point) that are of the same expected type.
I have the following code:
public abstract class BaseObject
{
public int Id { get; set; }
}
public class Father : BaseObject
{
public DateTime CreatedOn { get; set; }
public string Name { get; set; }
public IEnumerable<ChildA> Children1 { get; set; }
public IEnumerable<ChildB> Children2 { get; set; }
public IEnumerable<ChildA> Children3 { get; set; }
public IEnumerable<ChildB> Children4 { get; set; }
}
public class ChildA : BaseObject
{
public int Val1 { get; set; }
}
public class ChildB : BaseObject
{
public string Name { get; set; }
public int Total { get; set; }
}
I want to process an object by applying some changes on a specific property on the target object and on all property children that I explicit say:
public void Start()
{
var listA = new List<ChildA> { new ChildA { Id = 1, Val1 = 1 }, new ChildA { Id = 2, Val1 = 2 } };
var listB = new List<ChildB> { new ChildB { Id = 1, Name = "1", Total = 1 } };
var obj = new Father { Id = 1, CreatedOn = DateTime.Now, Name = "F1", ChildrenA = listA, ChildrenB = listB };
// I explicit tell to process only 2 of the 4 lists....
ProcessObj(obj, x => new object[] { x.Children1, x.Children2 });
}
I was able to write this function:
public void ProcessObj<T>(T obj, Expression<Func<T, object[]>> includes = null)
{
var objBaseObject = obj as BaseObject;
if (objBaseObject == null) return;
// Here I change the ID - add 100 just as an example....
objBaseObject.Id = objBaseObject.Id + 100;
if (includes == null) return;
var array = includes.Body as NewArrayExpression;
if (array == null) return;
var exps = ((IEnumerable<object>)array.Expressions).ToArray();
for (var i = 0; i < exps.Count(); i++)
{
var name = ((MemberExpression)exps[i]).Member.Name;
var childProperty = obj.GetType().GetProperties(
BindingFlags.Public | BindingFlags.Instance
).FirstOrDefault(prop => prop.Name == name);
if (childProperty == null) continue;
// NOT correct because I think I am getting a copy of the object
// and not pointing to the object in memory (by reference)
var childList = childProperty.GetValue(obj);
// TODO: loop on the list and apply the same logic as the father....
// change the ID field....
}
}
In this prototype I started writing reflection, but I really would like to avoid it if possible....
How can I do this???
Maybe I'm missing something, but it seems like you're complicating the problem by using expression trees. Can you just not use a regular Action and Func delegates to do this? Why do they need to be expression trees? Here's an example just using delegates:
public void ProcessObj<T>(T obj, Func<T, IEnumerable<object>> includes) {
var objBaseObject = obj as BaseObject;
if (objBaseObject == null) return;
// Create a reusable action to use on both the parent and the children
Action<BaseObject> action = x => x.Id += 100;
// Run the action against the root object
action(objBaseObject);
// Get the includes by just invoking the delegate. No need for trees.
var includes = includes(obj);
// Loop over each item in each collection. If the types then invoke the same action that we used on the root.
foreach(IEnumerable<object> include in includes)
{
foreach(object item in include)
{
var childBaseObject = item as BaseObject;
if(childBaseObject != null)
{
action(childBaseObject);
}
}
}
}
Useable just like before:
ProcessObj(obj, x => new object[] { x.Children1, x.Children2 });
No expression trees and no reflection, just regular delegate lambdas.
Hope that helps

.Net Get Property Values of Object by Key, to any depth

I would like to be able to access the value of an object property to any depth having only the string-key of the property. Also, if possible, using collection indexing on List properties.
So, If I have the string "Person.Surname" then I could get the value "Smith" from and instanciated CaseConductor object. So given some setup code like this ...
//- Load a caseConductor
var caseConductor = new CaseConductor();
caseConductor.CaseID = "A00001";
// person
caseConductor.Person = new Person();
caseConductor.Person.Surname = "Smith" ;
caseConductor.Person.DOB = DateTime.Now ;
// case note list
caseConductor.CaseNoteList = new List<Note>();
caseConductor.CaseNoteList.Add(new Note { NoteText = "A-1" , NoteDt = DateTime.Now });
caseConductor.CaseNoteList.Add(new Note { NoteText = "B-2", NoteDt = DateTime.Now });
// I could do this ...
object val = caseConductor.SomeCleverFunction("Person.Surname");
// or this ...
object val = caseConductor.SomeCleverFunction("CaseNoteList[0].NoteText");
Has anyone done this before ?
Here are some setup classes ...
class Note
{
public Guid NoteID { get; set; }
public string NoteText { get; set; }
public DateTime? NoteDt { get; set; }
}
public class Person
{
public Guid PersonID { get; set; }
public string Surname { get; set; }
public string Forename { get; set; }
public DateTime? DOB { get; set; }
}
class CaseConductor
{
public String CaseID{get;set;}
public Person Person { get; set; }
public List<Note> CaseNoteList { get; set; }
}
Our use case is to iterate over a series of appropriately named content controls in a word dcoument template using open xml sdk 2, and poke values into a newly created word documents, something like this ...
List<SdtElement> ccList = wordprocessingDocument.MainDocumentPart.Document.Descendants<SdtElement>().ToList();
foreach (var cc in ccList)
{
string alias = cc.SdtProperties.GetFirstChild<SdtAlias>().Val.Value;
switch (cc.GetType().Name)
{
case "SdtRun":
SdtRun thisRun = (SdtRun)cc;
//thisRun.Descendants<Text>().First().Text = theValueToBePoked ;
break;
}
}
Use good old reflection. I have tested and this actually works:
public static object GetValue(object o, string propertyName)
{
Type type = o.GetType();
PropertyInfo propertyInfo = type.GetProperties(BindingFlags.Public | BindingFlags.Instance ).Where(x => x.Name == propertyName).FirstOrDefault();
if(propertyInfo!=null)
{
return propertyInfo.GetValue(o, BindingFlags.Instance, null, null, null);
}
else
{
return null; // or throw exception
}
}
I'm assuming that
caseConductor.SomeCleverFunction
is not a static method, and has access to the Person object, and that the Person object itself if a public property.
I'm also assuming that you want to pass a string like "prop.address.street" where each sub property is an class that containts a puplic property with that name
Split the string input on the period, find the left most string
Use reflection to get a list of properties ( typeof(caseconductor).GetProperties() )
Find the matching property, call GetValue on it, passing the last known solid object (starting with 'this') and storing a refernce to it.
if there is more sub properties in the string left, repeat to step 1, removing the left most part of the string.
otherwise, call GetValue() on the property, using the last GetValue() return object from step 3, and return it.
Something like:
"prop.address.street" -> find property "prop" from 'this' and GetValue,
there is still more "."'s so repeat, storing return value
"address.street" -> find property "address" from the last returned GetValue, and get it's value.
there is still more "."'s so repeat, storing return value
"street" -> find property "street" from the last returned GetValue, and return it's value.
End of string, return last value
Edit -
This is pretty rough, but toss it into LinqPAD and take a look.
http://www.linqpad.net/
Edit #2 - you should be able to index into arrays using the ^ syntax below.
Again this is reaaaaaaaaally rough, just enough to get a working example.
Edit #3 - Cleaned up the example slightly and changed it from my example classes to yours.
void Main()
{
//- Load a caseConductor
var caseConductor = new CaseConductor();
caseConductor.CaseID = "A00001";
// person
caseConductor.Person = new Person();
caseConductor.Person.Surname = "Smith" ;
caseConductor.Person.DOB = DateTime.Now ;
// case note list
caseConductor.CaseNoteList = new List<Note>();
caseConductor.CaseNoteList.Add(new Note { NoteText = "A-1" , NoteDt = DateTime.Now });
caseConductor.CaseNoteList.Add(new Note { NoteText = "B-2", NoteDt = DateTime.Now });
// I could do this ...
string val1 = caseConductor.GetPropertyValue<string>("Person.Surname");
// or this ...
Note val2 = caseConductor.GetPropertyValue<Note>("CaseNoteList^1");
val1.Dump("val1"); //this is a string
val2.Dump("val2"); //this is a Note
}
public static class extensions
{
public static T GetPropertyValue<T>(this object o,string Properties) where T:class
{
var properties = Properties.Split('.');
var indexsplit = properties[0].Split('^');
var current = indexsplit[0];
var prop = (from p in o.GetType().GetProperties() where p.Name == current select p).Take(1).Single();
var val = prop.GetValue(o,null);
if(indexsplit.Length>1)
{
var index = int.Parse(indexsplit[1]);
IList ival = (IList)val;
val = ival[index];
}
if(properties[0] == Properties)
return (T)val;
else
return val.GetPropertyValue<T>(Properties.Replace(properties[0]+".",""));
}
}
class Note
{
public Guid NoteID { get; set; }
public string NoteText { get; set; }
public DateTime? NoteDt { get; set; }
}
public class Person
{
public Guid PersonID { get; set; }
public string Surname { get; set; }
public string Forename { get; set; }
public DateTime? DOB { get; set; }
}
class CaseConductor
{
public String CaseID{get;set;}
public Person Person { get; set; }
public List<Note> CaseNoteList { get; set; }
}
OK, I came up with something which continues Aliosted and asowyer start suggestions, here it is. You can see I still having trouble with the index access of composed objects. Thnaks for your help.
#region object data ...
var model = new HcmlDocumentProductionModel();
model.CaseID = "A001";
model.CaseConductor = new CaseConductor();
model.CaseConductor.AField = "AField";
model.CaseConductor.Person = new Person();
model.CaseConductor.Person.Surname = "{Smith}";
model.CaseConductor.Person.DOB = DateTime.Now;
model.CaseConductor.CaseNoteList = new List<Note>();
model.CaseConductor.CaseNoteList.Add(new Note { NoteText = "A-1", NoteDt = DateTime.Now, NoteTypeEnum = NoteTypeEnum.CaseNote });
model.CaseConductor.CaseNoteList.Add(new Note { NoteText = "B-2", NoteDt = DateTime.Now, NoteTypeEnum = NoteTypeEnum.ReferralNote });
model.CaseConductor.CaseNoteList.Add(new Note { NoteText = "C-3", NoteDt = DateTime.Now, NoteTypeEnum = NoteTypeEnum.StatusNote });
model.CaseConductor.CaseNoteList.Add(new Note { NoteText = "d-3", NoteDt = DateTime.Now, NoteTypeEnum = NoteTypeEnum.CaseNote });
model.CaseConductor.CaseNoteList.Add(new Note { NoteText = "e-3", NoteDt = DateTime.Now, NoteTypeEnum = NoteTypeEnum.StatusNote });
model.CaseConductor.CaseNoteList.Add(new Note { NoteText = "f-3", NoteDt = DateTime.Now, NoteTypeEnum = NoteTypeEnum.CaseNote });
#endregion
string head = "";
string tail = "";
// tail
tail = "".Tail();
tail = "Surname".Tail();
tail = "Person.Surname".Tail();
tail = "CaseConductor.Person.Surname".Tail();
// head
head = "".Head();
head = "Surname".Head();
head = "Person.Surname".Head();
head = "CaseConductor.Person.Surname".Head();
// ObjectDictionary
//var person = new Person { Surname = "Smith" };
//var d = person.ObjectDictionary();
//object ovalue = d["Surname"];
// get value special
object o2 = model.CaseConductor.Person.ValueByKey("Surname");
object o3 = model.CaseConductor.Person.ValueByKey("DOB");
object o4 = model.CaseConductor.ValueByKey("Person.Surname");
object o5 = model.ValueByKey("CaseConductor.Person.Surname");
// get the list of ...
object o6 = model.ValueByKey("CaseConductor.CaseNoteList");
// get item - index thing does not work - get anull here
string noteText = model.CaseConductor.CaseNoteList[1].NoteText;
object o7 = model.ValueByKey("CaseConductor.CaseNoteList[1].NoteText");
namespace Zed
{
public static class Zed
{
public static object ValueByKey(this object o, string key)
{
if (!String.IsNullOrEmpty(key))
{
if (!key.Contains("."))
{
return (o.ObjectDictionary())[key];
}
else
{
// key contains a dot ; therefore get object by the name of the head
// and pass on that object and get propety by the tail
var d = o.ObjectDictionary();
var head = key.Head();
if (head.Contains("["))
{
string headMinusIndexer = head.Substring(0, head.IndexOf("["));
string indexString = head.Between("[", "]");
int index = Convert.ToInt32(indexString);
object oArray = d[headMinusIndexer];
//List<object> oList= d[headMinusIndexer];
// now get the object with the index, ... and continue
//object el = ((object[])oArray)[index];
return null;
}
else
{
var onext = d[head];
return onext.ValueByKey(key.Tail());
}
}
}
return null;
}
public static Dictionary<string,object> ObjectDictionary(this object o)
{
return o.GetType().GetProperties().ToDictionary(p => p.Name, p => p.GetValue(o, null));
}
public static string Head(this string key)
{
var head = String.Empty;
var splittBy = '.';
if (!String.IsNullOrEmpty(key))
{
var keyArray = key.Split(splittBy);
head = keyArray[0];
}
//-Return
return head;
}
public static string Tail(this string key)
{
var tail = "";
var splittBy = '.';
if (!String.IsNullOrEmpty(key))
{
var keyArray = key.Split(splittBy);
for (int i = 1; i < keyArray.Length; i++)
{
tail += (i > 1) ? "." + keyArray[i] : keyArray[i];
}
}
//-Return
return tail;
}
public static string Between(this string head, string start, string end)
{
string between = String.Empty ;
between = head.Substring(head.IndexOf(start) + 1, head.IndexOf(end) - head.IndexOf(start) - 1);
return between;
}
public static object ZGetValue( this object o, string propertyName)
{
Type type = o.GetType();
PropertyInfo propertyInfo = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.Name == propertyName).FirstOrDefault();
if (propertyInfo != null)
{
return propertyInfo.GetValue(o, BindingFlags.Instance, null, null, null);
}
else
{
return null;
}
}
}
}

Categories