Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert nested class to dictionary

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?

like image 935
Raskolnikoov Avatar asked Jul 09 '15 10:07

Raskolnikoov


2 Answers

(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");
like image 170
Buh Buh Avatar answered Nov 13 '22 00:11

Buh Buh


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));
    }
like image 29
Nguyen Minh Hien Avatar answered Nov 12 '22 22:11

Nguyen Minh Hien