I have a list of properties and their values and they are formatted in a Dictionary<string, object>
like this:
Person.Name = "John Doe"
Person.Age = 27
Person.Address.House = "123"
Person.Address.Street = "Fake Street"
Person.Address.City = "Nowhere"
Person.Address.State = "NH"
There are two classes. Person
is composed of the string Name
and primitive Age
as well as the complex Address
class which has House
, Street
, City
, and State
string properties.
Basically what I want to do is look up the class Person
in the current assembly and create an instance of it and assign all of the values, no matter how complicated the classes get, as long as at the deepest level they are composed of primitives, strings, and a few common structures such as DateTime
.
I have a solution which allows me to assign the top level properties and down into one of the complex properties. I am assuming I must use recursion to solve this, but I would prefer not to.
Though, even with recursion, I am at a loss as to a nice way to get down into each of the properties and assign their values.
In this example below I am trying to translate the dotted representation to classes based on a method's parameters. I look up the appropriate dotted representation based on the parameter's type, trying to find a match. DotField
is basically a KeyValuePair<string, object>
where the key is the Name
property. The code below may not work correctly but it should express the idea well enough.
foreach (ParameterInfo parameter in this.method.Parameters)
{
Type parameterType = parameter.ParameterType;
object parameterInstance = Activator.CreateInstance(parameterType);
PropertyInfo[] properties = parameterType.GetProperties();
foreach (PropertyInfo property in properties)
{
Type propertyType = property.PropertyType;
if (propertyType.IsPrimitive || propertyType == typeof(string))
{
string propertyPath = String.Format("{0}.{1}", parameterType.Name, propertyType.Name);
foreach (DotField df in this.DotFields)
{
if (df.Name == propertyPath)
{
property.SetValue(parameterInstance, df.Value, null);
break;
}
}
}
else
{
// Somehow dive into the class, since it's a non-primitive
}
}
}
Your Dictionary
sounds similar to JSON-formatted data. If you first transform it into a compatible form, you can use Json.Net to convert the dictionary into your object. Here's an example of that:
public static void Main()
{
var dict = new Dictionary<string, object>
{
{"Person.Name", "John Doe"},
{"Person.Age", 27},
{"Person.Address.House", "123"},
{"Person.Address.Street", "Fake Street"},
{"Person.Address.City", "Nowhere"},
{"Person.Address.State", "NH"},
};
var hierarchicalDict = GetItemAndChildren(dict, "Person");
string json = JsonConvert.SerializeObject(hierarchicalDict);
Person person = JsonConvert.DeserializeObject<Person>(json);
// person has all of the values you'd expect
}
static object GetItemAndChildren(Dictionary<string, object> dict, string prefix = "")
{
object val;
if (dict.TryGetValue(prefix, out val))
return val;
else
{
if (!string.IsNullOrEmpty(prefix))
prefix += ".";
var children = new Dictionary<string, object>();
foreach (var child in dict.Where(x => x.Key.StartsWith(prefix)).Select(x => x.Key.Substring(prefix.Length).Split(new[] { '.' }, 2)[0]).Distinct())
{
children[child] = GetItemAndChildren(dict, prefix + child);
}
return children;
}
}
You could also use reflection for doing so. I had fun writing this down :)
private object Eval(KeyValuePair<string, object> df)
{
var properties = df.Key.Split('.');
//line below just creates the root object (Person), you could replace it with whatever works in your example
object root = Activator.CreateInstance(Assembly.GetExecutingAssembly().GetTypes().First(t => t.Name == properties.First()));
var temp = root;
for (int i = 1; i < properties.Length - 1; i++)
{
var propertyInfo = temp.GetType().GetProperty(properties[i]);
var propertyInstance = Activator.CreateInstance(propertyInfo.PropertyType);
propertyInfo.SetValue(temp, propertyInstance, null);
temp = propertyInstance;
}
temp.GetType().GetProperty(properties.Last()).SetValue(temp, df.Value, null);
return root;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With