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 = "Smi开发者_C百科th" ;
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;
}
}
}
}
精彩评论