Nesneyi sözlüğe eşleme ve tersi


91

Nesneyi bir sözlüğe veya tam tersine eşlemenin zarif ve hızlı bir yolu var mı?

Misal:

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

olur

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

en hızlı yol protobuf gibi kod üretmektir ... Yansımayı olabildiğince sık önlemek için ifade ağaçları kullandım
Konrad

Aşağıdaki tüm kendi kendine kodlanmış cevaplar derin dönüşümü desteklemez ve gerçek hayattaki uygulamalarda çalışmayacaktır. Earnerplates tarafından önerilen Newtonsoft gibi bir kitaplığı kullanmalısınız
Cesar,

Yanıtlar:


175

İki uzatma yönteminde biraz yansıma ve jenerik kullanarak bunu başarabilirsiniz.

Doğru, diğerleri çoğunlukla aynı çözümü yaptılar, ancak bu daha az yansıtma kullanır, bu daha performans açısından daha fazla ve çok daha okunabilirdir:

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

Uygulamayı beğendim, gerçekten kullanmaya başladım, ancak performansla ilgili herhangi bir geri bildiriminiz var mı? Performans söz konusu olduğunda yansımanın en iyisi olmadığını biliyorum. herhangi bir yorumun var mı?
nolimit

@nolimit Aslında gerçek performansını bilmiyorum ama sanırım o kadar da kötü değil (tabi ki yansımadan kaçınmaktan daha kötü ...). Aslında alternatif nedir? Projelerin gereksinimlerine bağlı olarak, yansımadan çok daha hızlı olan dinamik yazmayı kullanmak gibi başka seçenekler de olabilir ... Kim bilir! :)
Matías Fidemraizer

benim durumumda alternatif, nesneyi atama yoluyla oluşturmak kadar basit, ancak bir nesne oluşturmanın temiz ama ucuz bir yolunu arıyorum. Bunu söyledikten sonra, bunu buldum, bu da benim için o kod parçasını kullanmanın çok pahalı olmadığı anlamına geliyor.
nolimit

@nolimit Evet, sorun yok gibi görünüyor. Bu sadece gerekli sorun için doğru aracı kullanmakla ilgilidir: D Sizin durumunuzda, bir cazibe gibi çalışmalıdır. Henüz kodda bir şeyler ayarlayabileceğime inanıyorum!
Matías Fidemraizer

@nolimit artık olmadığını kontrol GetType()üzerinde çağrılmaz someObjectilk yöntemde artık her özellik için.
Matías Fidemraizer

31

Sözlüğü önce Newtonsoft ile JSON dizesine dönüştürün.

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

Ardından, nesnenize yönelik JSON dizesini seri durumdan kaldırın

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

1
Basit ve yaratıcı!
kamalpreet

1
Bu yaklaşımı dikkatli kullanın. Birçok (yüzlerce) sözlük için kullanılırsa uygulamanızı batırır.
amackay11

12

Yansıma sadece burada işe yarıyor gibi görünüyor .. Nesneyi sözlüğe dönüştürmek için küçük bir örnek yaptım ve bunun tersi de:

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

Bu, yuvalanmayı desteklemez.
Cesar

10

Bu projenin en iyi korunan sırlarından biri olan Castle DictionaryAdapter'ı şiddetle tavsiye ederim . Yalnızca istediğiniz özelliklere sahip bir arabirim tanımlamanız gerekir ve bir kod satırında adaptör bir uygulama oluşturur, onu başlatır ve değerlerini ilettiğiniz bir sözlükle senkronize eder. bir web projesi:

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

IAppSettings'i uygulayan bir sınıf oluşturmama gerek olmadığını unutmayın - adaptör bunu anında yapar. Ayrıca, bu durumda sadece okuyorum, teorik olarak appSettings'de özellik değerlerini ayarlıyorsam, bağdaştırıcı temeldeki sözlüğü bu değişikliklerle senkronize halde tutacaktır.


2
Sanırım "en iyi korunan sır" yalnız bir ölümle öldü. Castle DictionaryAdapter bağlantısı ve castleproject.org'daki "DictionaryAdapter" için İndirme bağlantılarının tümü bir 404 Sayfasına gider :(
Shiva

1
Hayır! Hala Castle.Core'da. Bağlantıyı düzelttim.
Gaspar Nagy

Castle, bir şekilde gözden kaçan harika bir kütüphaneydi
bikeman868

5

Yansıma, özellikleri yineleyerek sizi bir nesneden sözlüğe götürebilir.

Diğer yöne gitmek için , C # içinde dinamik bir ExpandoObject (aslında zaten IDictionary'den miras alan ve bunu sizin için yaptı) kullanmanız gerekir; sözlük bir şekilde.

Öyleyse, .NET 4.0 arazisindeyseniz, bir ExpandoObject kullanın, aksi takdirde yapacak çok işiniz vardır ...


4

Bence yansımayı kullanmalısın. Bunun gibi bir şey:

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

Sözlüğünüzü alır ve içinden geçer ve değerleri belirler. Daha iyi yapmalısın ama bu bir başlangıç. Buna şöyle demelisin:

SomeClass someClass = ConvertDictionaryTo<SomeClass>(a);

"Yeni" genel kısıtlamayı kullanabiliyorsanız, bir aktivatörü kullanmak istediğiniz şey için? :)
Matías Fidemraizer

@ Matías Fidemraizer Kesinlikle haklısınız. Benim hatam. Değiştirdi.
TurBas

Teşekkürler harika, bir sorun eğer bir sınıf sözlükte bulunan özelliklerden bazılarına sahip değilse. Yalnızca bir sınıfta var olan özelliklerle eşleşmeli ve diğerlerini atlamalıdır, şimdi deniyorum, bunun için daha iyi bir seçenek yakalamaya çalıştım?
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;
    }
}

3

Matías Fidemraizer'ın cevabına dayanarak, burada dizeler dışındaki nesne özelliklerine bağlanmayı destekleyen bir sürüm var.

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

Asp.Net MVC kullanıyorsanız, şuna bir göz atın:

public static RouteValueDictionary AnonymousObjectToHtmlAttributes(object htmlAttributes);

System.Web.Mvc.HtmlHelper sınıfında statik bir genel yöntem olan.


"_" "-" ile değiştirildiği için kullanmayacağım. Ve MVC'ye ihtiyacınız var. Saf Yönlendirme sınıfını kullanın: 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);
        }
    }

Bu kod soruyu yanıtlayabilirken, bu kodun soruyu neden ve / veya nasıl yanıtladığına ilişkin ek bağlam sağlamak, uzun vadeli değerini artırır.
baikho
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.