Ánh xạ đối tượng vào từ điển và ngược lại


90

Có cách nhanh chóng thanh lịch nào để ánh xạ đối tượng vào từ điển và ngược lại không?

Thí dụ:

IDictionary<string,object> a = new Dictionary<string,object>();
a["Id"]=1;
a["Name"]="Ahmad";
// .....

trở thành

SomeClass b = new SomeClass();
b.Id=1;
b.Name="Ahmad";
// ..........

cách nhanh nhất sẽ là tạo mã như protobuf ... Tôi đã sử dụng cây biểu thức để tránh phản chiếu thường xuyên nhất có thể
Konrad

Tất cả các câu trả lời tự mã hóa dưới đây không hỗ trợ chuyển đổi sâu và sẽ không hoạt động trong các ứng dụng đời thực. Bạn nên sử dụng một số thư viện như Newtonsoft theo đề xuất của người kiếm tiền
Cesar

Câu trả lời:


175

Sử dụng một số phản ánh và số liệu chung trong hai phương pháp mở rộng, bạn có thể đạt được điều đó.

Đúng, những người khác hầu hết đã làm cùng một giải pháp, nhưng điều này sử dụng ít phản ánh hơn, hiệu suất cao hơn và dễ đọc hơn:

public static class ObjectExtensions
{
    public static T ToObject<T>(this IDictionary<string, object> source)
        where T : class, new()
    {
            var someObject = new T();
            var someObjectType = someObject.GetType();

            foreach (var item in source)
            {
                someObjectType
                         .GetProperty(item.Key)
                         .SetValue(someObject, item.Value, null);
            }

            return someObject;
    }

    public static IDictionary<string, object> AsDictionary(this object source, BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)
    {
        return source.GetType().GetProperties(bindingAttr).ToDictionary
        (
            propInfo => propInfo.Name,
            propInfo => propInfo.GetValue(source, null)
        );

    }
}

class A
{
    public string Prop1
    {
        get;
        set;
    }

    public int Prop2
    {
        get;
        set;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Dictionary<string, object> dictionary = new Dictionary<string, object>();
        dictionary.Add("Prop1", "hello world!");
        dictionary.Add("Prop2", 3893);
        A someObject = dictionary.ToObject<A>();

        IDictionary<string, object> objectBackToDictionary = someObject.AsDictionary();
    }
}

Tôi thích việc triển khai, tôi thực sự đã bắt đầu sử dụng nó, nhưng bạn có bất kỳ phản hồi nào về hiệu suất không? Tôi biết rằng phản xạ không phải là lớn nhất khi nói đến hiệu suất? Bạn có ý kiến ​​nào hay không?
nolimit

@nolimit Thực ra tôi không biết hiệu suất thực tế của nó, nhưng tôi đoán nó không tệ lắm (tất nhiên là tệ nhất là tránh phản chiếu ...). Thực tế, đâu là giải pháp thay thế? Tùy thuộc vào yêu cầu của dự án, có thể có các tùy chọn khác, như sử dụng nhập động nhanh hơn nhiều so với phản chiếu ... Ai biết được! :)
Matías Fidemraizer

Trong trường hợp của tôi, giải pháp thay thế cũng đơn giản như xây dựng đối tượng bằng cách gán, nhưng tôi đang tìm một cách gọn gàng nhưng không tốn kém để xây dựng một đối tượng. Phải nói rằng, tôi thấy điều này có nghĩa là đối với tôi, không quá đắt để sử dụng đoạn mã đó.
nolimit

@nolimit Vâng, có vẻ như không có vấn đề gì. Nó chỉ là sử dụng đúng công cụ cho vấn đề cần thiết: D Trong trường hợp của bạn, nó sẽ hoạt động như một lá bùa. Tôi tin rằng tôi có thể điều chỉnh một cái gì đó trong mã!
Matías Fidemraizer

@nolimit Kiểm tra xem bây giờ GetType()không được gọi someObjectcho từng thuộc tính nữa trong phương thức đầu tiên.
Matías Fidemraizer

30

Chuyển đổi Từ điển sang chuỗi JSON trước tiên với Newtonsoft.

var json = JsonConvert.SerializeObject(advancedSettingsDictionary, Newtonsoft.Json.Formatting.Indented);

Sau đó giải mã hóa chuỗi JSON thành đối tượng của bạn

var myobject = JsonConvert.DeserializeObject<AOCAdvancedSettings>(json);

1
Đơn giản và sáng tạo!
kamalpreet

1
Sử dụng phương pháp này một cách thận trọng. Nó sẽ làm hỏng ứng dụng của bạn nếu được sử dụng cho nhiều (hàng trăm) từ điển.
amackay11

12

Có vẻ như phản ánh chỉ giúp ích ở đây .. Tôi đã thực hiện một ví dụ nhỏ về chuyển đổi đối tượng thành từ điển và ngược lại:

[TestMethod]
public void DictionaryTest()
{
    var item = new SomeCLass { Id = "1", Name = "name1" };
    IDictionary<string, object> dict = ObjectToDictionary<SomeCLass>(item);
    var obj = ObjectFromDictionary<SomeCLass>(dict);
}

private T ObjectFromDictionary<T>(IDictionary<string, object> dict)
    where T : class 
{
    Type type = typeof(T);
    T result = (T)Activator.CreateInstance(type);
    foreach (var item in dict)
    {
        type.GetProperty(item.Key).SetValue(result, item.Value, null);
    }
    return result;
}

private IDictionary<string, object> ObjectToDictionary<T>(T item)
    where T: class
{
    Type myObjectType = item.GetType();
    IDictionary<string, object> dict = new Dictionary<string, object>();
    var indexer = new object[0];
    PropertyInfo[] properties = myObjectType.GetProperties();
    foreach (var info in properties)
    {
        var value = info.GetValue(item, indexer);
        dict.Add(info.Name, value);
    }
    return dict;
}

Điều này không hỗ trợ lồng vào nhau.
Cesar

10

Tôi rất muốn giới thiệu Castle DictionaryAdapter , dễ dàng là một trong những bí mật được giữ tốt nhất của dự án đó. Bạn chỉ cần xác định một giao diện với các thuộc tính bạn muốn và trong một dòng mã, bộ điều hợp sẽ tạo ra một triển khai, khởi tạo nó và đồng bộ hóa các giá trị của nó với từ điển mà bạn chuyển vào. Tôi sử dụng nó để nhập mạnh AppSettings của mình vào một dự án web:

var appSettings =
  new DictionaryAdapterFactory().GetAdapter<IAppSettings>(ConfigurationManager.AppSettings);

Lưu ý rằng tôi không cần phải tạo một lớp triển khai IAppSettings - bộ điều hợp thực hiện điều đó một cách nhanh chóng. Ngoài ra, mặc dù trong trường hợp này tôi chỉ đang đọc, về lý thuyết nếu tôi đang đặt giá trị thuộc tính trên appSettings, bộ điều hợp sẽ giữ cho từ điển bên dưới đồng bộ với những thay đổi đó.


2
Tôi đoán rằng "bí mật được giữ kín nhất" đã chết một cái chết cô đơn. Liên kết Castle DictionaryAdapter ở trên, cũng như các liên kết Tải xuống cho "DictionaryAdapter" trên Castleproject.org, tất cả đều đi đến Trang 404 :(
Shiva

1
Không! Nó vẫn ở trong Lâu đài. Tôi đã sửa liên kết.
Gaspar Nagy

Lâu đài là một thư viện tuyệt vời mà Got bỏ qua bằng cách nào đó
bikeman868

5

Reflection có thể đưa bạn từ một đối tượng đến từ điển bằng cách lặp lại các thuộc tính.

Để đi theo cách khác, bạn sẽ phải sử dụng một ExpandoObject động (trên thực tế, đã kế thừa từ IDictionary và vì vậy đã thực hiện điều này cho bạn) trong C #, trừ khi bạn có thể suy ra loại từ tập hợp các mục nhập trong từ điển nào đó.

Vì vậy, nếu bạn đang ở vùng đất .NET 4.0, hãy sử dụng ExpandoObject, nếu không, bạn có rất nhiều việc phải làm ...


4

Tôi nghĩ bạn nên sử dụng sự phản chiếu. Một cái gì đó như thế này:

private T ConvertDictionaryTo<T>(IDictionary<string, object> dictionary) where T : new()
{
    Type type = typeof (T);
    T ret = new T();

    foreach (var keyValue in dictionary)
    {
        type.GetProperty(keyValue.Key).SetValue(ret, keyValue.Value, null);
    }

    return ret;
}

Nó lấy từ điển của bạn và lặp qua nó và đặt các giá trị. Bạn nên làm cho nó tốt hơn nhưng đó là một sự khởi đầu. Bạn nên gọi nó như thế này:

SomeClass someClass = ConvertDictionaryTo<SomeClass>(a);

Đối với những gì bạn muốn sử dụng một trình kích hoạt nếu bạn có thể sử dụng ràng buộc chung "mới"? :)
Matías Fidemraizer

@ Matías Fidemraizer Bạn hoàn toàn đúng. Lỗi của tôi. Đã thay đổi nó.
TurBas

Cảm ơn bạn, tuyệt vời, một vấn đề nếu lớp nào đó không có một số thuộc tính có trong từ điển. Nó sẽ chỉ khớp với các thuộc tính tồn tại trong lớp nào đó và bỏ qua lớp khác, như bây giờ tôi đã sử dụng, hãy thử bắt bất kỳ tùy chọn nào tốt hơn cho điều này?
Sunil Chaudhary

3
public class SimpleObjectDictionaryMapper<TObject>
{
    public static TObject GetObject(IDictionary<string, object> d)
    {
        PropertyInfo[] props = typeof(TObject).GetProperties();
        TObject res = Activator.CreateInstance<TObject>();
        for (int i = 0; i < props.Length; i++)
        {
            if (props[i].CanWrite && d.ContainsKey(props[i].Name))
            {
                props[i].SetValue(res, d[props[i].Name], null);
            }
        }
        return res;
    }

    public static IDictionary<string, object> GetDictionary(TObject o)
    {
        IDictionary<string, object> res = new Dictionary<string, object>();
        PropertyInfo[] props = typeof(TObject).GetProperties();
        for (int i = 0; i < props.Length; i++)
        {
            if (props[i].CanRead)
            {
                res.Add(props[i].Name, props[i].GetValue(o, null));
            }
        }
        return res;
    }
}

2

Dựa trên câu trả lời của Matías Fidemraizer, đây là một phiên bản hỗ trợ ràng buộc với các thuộc tính đối tượng không phải là chuỗi.

using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace WebOpsApi.Shared.Helpers
{
    public static class MappingExtension
    {
        public static T ToObject<T>(this IDictionary<string, object> source)
            where T : class, new()
        {
            var someObject = new T();
            var someObjectType = someObject.GetType();

            foreach (var item in source)
            {
                var key = char.ToUpper(item.Key[0]) + item.Key.Substring(1);
                var targetProperty = someObjectType.GetProperty(key);


                if (targetProperty.PropertyType == typeof (string))
                {
                    targetProperty.SetValue(someObject, item.Value);
                }
                else
                {

                    var parseMethod = targetProperty.PropertyType.GetMethod("TryParse",
                        BindingFlags.Public | BindingFlags.Static, null,
                        new[] {typeof (string), targetProperty.PropertyType.MakeByRefType()}, null);

                    if (parseMethod != null)
                    {
                        var parameters = new[] { item.Value, null };
                        var success = (bool)parseMethod.Invoke(null, parameters);
                        if (success)
                        {
                            targetProperty.SetValue(someObject, parameters[1]);
                        }

                    }
                }
            }

            return someObject;
        }

        public static IDictionary<string, object> AsDictionary(this object source, BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)
        {
            return source.GetType().GetProperties(bindingAttr).ToDictionary
            (
                propInfo => propInfo.Name,
                propInfo => propInfo.GetValue(source, null)
            );
        }
    }
}

1

Nếu bạn đang sử dụng Asp.Net MVC, hãy xem:

public static RouteValueDictionary AnonymousObjectToHtmlAttributes(object htmlAttributes);

là một phương thức công khai tĩnh trên lớp System.Web.Mvc.HtmlHelper.


Tôi sẽ không sử dụng nó vì nó thay thế "_" bằng "-". Và bạn cần MVC. Sử dụng lớp Định tuyến thuần túy: new RouteValueDictionary (objectHere);
regisbsb

0
    public Dictionary<string, object> ToDictionary<T>(string key, T value)
    {
        try
        {
            var payload = new Dictionary<string, object>
            {
                { key, value }
            }; 
        } catch (Exception e)
        {
            return null;
        }
    }

    public T FromDictionary<T>(Dictionary<string, object> payload, string key)
    {
        try
        {
            JObject jObject = (JObject) payload[key];
            T t = jObject.ToObject<T>();
            return (t);
        }
        catch(Exception e) {
            return default(T);
        }
    }

Mặc dù mã này có thể trả lời câu hỏi, nhưng việc cung cấp thêm ngữ cảnh liên quan đến lý do và / hoặc cách mã này trả lời câu hỏi sẽ cải thiện giá trị lâu dài của nó.
baikho 28/07/18
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.