将嵌套类转换为字典

本文关键字:字典 转换 嵌套 | 更新日期: 2023-09-27 18:01:43

我想将一个嵌套类转换为一个漂亮的键/值字典,并保留每个属性的类名和嵌套路径,以便以后方便搜索。

这是我的示例类:

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

这是我期望的键/值在字典中的输出:

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

有人知道我怎么才能做到这一点吗?

将嵌套类转换为字典

(抱歉,我没有时间测试这个)

你可以用反射和递归写一个解决方案。就像下面这样。

您将需要添加空检查和其他退出情况,否则您将很快结束在无限循环这只是一个开始。

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

这样写:

Dictionary<string, string> dictionary2 = MapToDictionary(agreement, "Agreement");

感谢Buh Buh,他的解决方案非常有效。只是增加一些配置和旁路环路深度。

//定义方法

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

//调用

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

更新缺失的方法

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

Buh Buh的解决方案非常有效,但需要进行小的调整。我做了一点改变,因为当属性类型为DateTime时它不起作用。

    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;
            if (value == null)
            {
                dictionary[key] = null;
            }
            else
            {
                Type valueType = value.GetType();
                if (valueType.IsPrimitive || valueType == typeof(String) || valueType == typeof(DateTime))
                {
                    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);
                }
            }
        }
    }