I want to convert a nested class to a nice key/value dictionary and keep the class names and nested paths for each property to make it easy searchable for later.
This is my example class:
var agreement = new Agreement
{
ID = 101,
Description = "Convert to dictionary",
Customer = new Customer
{
FirstName = "John",
LastName = "Smith",
Age = 30,
Address = new List<Address>()
{
new Address
{
Name = "Los Angeles 1",
ZipCode = 25437
},
new Address
{
Name = "New York 25",
ZipCode = 25441
}
}
}
};
And this is my expected key/value output in the dictionary:
KEY VALUE
Agreement.ID: 101
Agreement.Description: Convert to dictionary
Agreement.Customer.FirstName: John
Agreement.Customer.LastName: Smith
Agreement.Customer.Age: 30
Agreement.Customer.Address[0].Name: Los Angeles 1
Agreement.Customer.Address[0].ZipCode: 25437
Agreement.Customer.Address[1].Name: New York 25
Agreement.Customer.Address[1].ZipCode: 25441
Anyone has an idea how I could achieve this?
(Apologies, I dont have time to test this.)
You could write a solution using reflection and recursion. Something like this, below.
You will want to add null checks, and other exit cases otherwise you will quickly end up in an infinate loop. This is just to get you started.
public Dictionary<string, string> MapToDictionary(object source, string name)
{
var dictionary = new Dictionary<string, string>();
MapToDictionaryInternal(dictionary, source, name);
return dictionary;
}
private void MapToDictionaryInternal(
Dictionary<string, string> dictionary, object source, string name)
{
var properties = source.GetType().GetProperties();
foreach(var p in properties)
{
var key = name + "." + p.Name;
object value = p.GetValue(source, null);
Type valueType = value.GetType();
if (valueType.IsPrimitive || valueType == typeof (String))
{
dictionary[key] = value.ToString();
}
else if (value is IEnumerable)
{
var i = 0;
foreach (object o in (IEnumerable) value)
{
MapToDictionaryInternal(dictionary, o, key + "[" + i + "]");
i++;
}
}
else
{
MapToDictionaryInternal(dictionary, value, key);
}
}
}
Call it like this:
Dictionary<string, string> dictionary2 = MapToDictionary(agreement, "Agreement");
Thank to Buh Buh, his solution works great. Just adding some configurations and bypass loop deep.
// Defining methods
public class ObjectConvertInfo
{
public object ConvertObject { set; get; }
public IList<Type> IgnoreTypes { set; get; }
public IList<string> IgnoreProperties { set; get; }
public int MaxDeep { set; get; } = 3;
}
public Dictionary<string, string> ConvertObjectToDictionary(ObjectConvertInfo objectConvertInfo)
{
try
{
var dictionary = new Dictionary<string, string>();
MapToDictionaryInternal(dictionary, objectConvertInfo, objectConvertInfo.ConvertObject.GetType().Name, 0);
return dictionary;
}
catch (Exception e)
{
return null;
}
}
private void MapToDictionaryInternal(IDictionary<string, string> dictionary, ObjectConvertInfo objectConvertInfo, string name, int deep)
{
try
{
if (deep > objectConvertInfo.MaxDeep)
return;
var properties = objectConvertInfo.ConvertObject.GetType().GetProperties();
foreach (var propertyInfo in properties)
{
if (objectConvertInfo.IgnoreProperties.ContainIgnoreCase(propertyInfo.Name))
continue;
var key = name + "." + propertyInfo.Name;
var value = propertyInfo.GetValue(objectConvertInfo.ConvertObject, null);
if (value == null)
continue;
var valueType = value.GetType();
if (objectConvertInfo.IgnoreTypes.Contains(valueType))
continue;
if (valueType.IsPrimitive || valueType == typeof(String))
{
dictionary[key] = value.ToString();
}
else if (value is IEnumerable)
{
var i = 0;
foreach (var data in (IEnumerable)value)
{
MapToDictionaryInternal(dictionary, new ObjectConvertInfo
{
ConvertObject = data,
IgnoreTypes = objectConvertInfo.IgnoreTypes,
IgnoreProperties = objectConvertInfo.IgnoreProperties, MaxDeep = objectConvertInfo.MaxDeep
}, key + "[" + i + "]", deep + 1);
i++;
}
}
else
{
MapToDictionaryInternal(dictionary, new ObjectConvertInfo
{
ConvertObject = value,
IgnoreTypes = objectConvertInfo.IgnoreTypes,
IgnoreProperties = objectConvertInfo.IgnoreProperties, MaxDeep = objectConvertInfo.MaxDeep
}, key, deep + 1);
}
}
}
catch (Exception ex)
{
}
}
// And call
var result = cryptoService.ConvertObjectToDictionary(new ObjectConvertInfo
{
ConvertObject = objectToWrite,
IgnoreProperties = new List<string> { "PropertyA", "PropertyB"},
IgnoreTypes = new List<Type> { typeof(IntPtr), typeof(Delegate), typeof(Type) },
MaxDeep = 3
});
Update for missing method
public static bool ContainIgnoreCase(this IEnumerable<string> list, string value)
{
if (list == null || !list.Any())
return false;
if (value == null)
return false;
return list.Any(item => item.Equals(value, StringComparison.OrdinalIgnoreCase));
}
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