Mapping Object to Dictionary and Vice Versa

Mapping object to dictionary and vice versa

Using some reflection and generics in two extension methods you can achieve that.

Right, others did mostly the same solution, but this uses less reflection which is more performance-wise and way more readable:

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

Dictionary string,string map to an object using Automapper

AutoMapper maps between properties of objects and is not supposed to operate in such scenarios. In this case you need Reflection magic. You could cheat by an intermediate serialization:

var data = new Dictionary<string, string>();
data.Add("Name", "Rusi");
data.Add("Age", "23");
var serializer = new JavaScriptSerializer();
var user = serializer.Deserialize<User>(serializer.Serialize(data));

And if you insist on using AutoMapper you could for example do something along the lines of:

Mapper
.CreateMap<Dictionary<string, string>, User>()
.ConvertUsing(x =>
{
var serializer = new JavaScriptSerializer();
return serializer.Deserialize<User>(serializer.Serialize(x));
});

and then:

var data = new Dictionary<string, string>();
data.Add("Name", "Rusi");
data.Add("Age", "23");
var user = Mapper.Map<Dictionary<string, string>, User>(data);

If you need to handle more complex object hierarchies with sub-objects you must ask yourself the following question: Is Dictionary<string, string> the correct data structure to use in this case?

How to map a property of type Dictionary string, MyDto using fluent mapping

It's not possible to add this as an explicit mapping, but it is through a dynamic template.

Let's look at why it's not possible through explicit mapping. Consider how Dictionary<string, MyDto> SomeProperty will serialize to JSON. For example

client.IndexDocument(new MyEntity 
{
SomeProperty = new Dictionary<string, UserQuery.MyDto>
{
{ "field_1", new MyDto { Name = "foo" } },
{ "field_2", new MyDto { Name = "bar" } }
}
});

by default will serialize as

{
"someProperty": {
"field_1": {
"name": "foo"
},
"field_2": {
"name": "bar"
}
}
}

If we wanted to apply an explicit mapping to MyDto.Name, we would need to know at the point of mapping, all of the dictionary keys that will be used.

You can however, configure a dynamic template that will map any MyDto.Name as a keyword type, using path_match

private static void Main()
{
var defaultIndex = "my_index";
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));

var settings = new ConnectionSettings(pool)
.DefaultIndex(defaultIndex);

var client = new ElasticClient(settings);

if (client.IndexExists(defaultIndex).Exists)
client.DeleteIndex(defaultIndex);

var createIndexResponse = client.CreateIndex(defaultIndex, c => c
.Settings(s => s
.NumberOfShards(1)
.NumberOfReplicas(0)
)
.Mappings(m => m
.Map<MyEntity>(mm => mm
.AutoMap()
.DynamicTemplates(dt => dt
.DynamicTemplate("MyDto", dtd => dtd
.PathMatch("someProperty.*.name")
.Mapping(dm => dm
.Keyword(k => k)
)
)
)
.Properties(p => p
.Object<Dictionary<string, MyDto>>(o => o
.Name(n => n.SomeProperty)
)
)
)
)
);

var indexResponse = client.Index(new MyEntity
{
SomeProperty = new Dictionary<string, UserQuery.MyDto>
{
{ "field_1", new MyDto { Name = "foo" } },
{ "field_2", new MyDto { Name = "bar" } }
}
}, i => i.Refresh(Refresh.WaitFor));

var mappingResponse = client.GetMapping<MyEntity>();
}

public class MyEntity
{
public Dictionary<string, MyDto> SomeProperty { get; set; }
}

public class MyDto
{
public string Name { get; set; }
}

The mapping response confirms that someProperty.field_1.name and someProperty.field_2.name are mapped as keyword

{
"my_index" : {
"mappings" : {
"myentity" : {
"dynamic_templates" : [
{
"MyDto" : {
"path_match" : "someProperty.*.name",
"mapping" : {
"type" : "keyword"
}
}
}
],
"properties" : {
"someProperty" : {
"properties" : {
"field_1" : {
"properties" : {
"name" : {
"type" : "keyword"
}
}
},
"field_2" : {
"properties" : {
"name" : {
"type" : "keyword"
}
}
}
}
}
}
}
}
}
}

An aside on mapping explosion

You may want to consider adding a property to MyDto to hold the dictionary key, and use a List<MyDto> or similar collection mapped as a nested datatype, rather than Dictionary<string, MyDto>, if users can add any arbitrary key names they wish. With a high cardinality of dictionary keys, you run the risk of a mapping explosion and hitting the maximum number of fields soft limit and a lot of sparse fields, which can affect performance. With a List<MyDto> property type, you would not have this issue and can still query on the key field, at the expense of the trade-off that a List<MyDto> may be less optimal for your application code than a Dictionary<string, MyDto>. Something to consider :)

F# efficient way to convert Map into Dict (and vice versa)

There isn't a convenient function to go from dictionary to map but there is a built in dict function which converts from a sequence of tuples to an IDictionary<'a,'b>, see: https://msdn.microsoft.com/en-us/library/ee353774.aspx.

The simplest way I know to do the dictionary to map conversion is:

let dictToMap (dic : System.Collections.Generic.IDictionary<_,_>) = 
dic
|> Seq.map (|KeyValue|)
|> Map.ofSeq

The reverse is easy with the dict function:

let mapToDict map =
map
|> Map.toSeq
|> dict

Note that the above function returns an IDictionary, the implementation of which is both internal to the FSharp.Core library and not mutable.

If you want to get to a standard .NET mutable dictionary, you use the constructor which accepts an IDictionary, Map implements IDictionary so this step is very straightforward.

let mapToMutDict map =
map :> System.Collections.Generic.IDictionary<_,_>
|> System.Collections.Generic.Dictionary

Python dictionary from an object's fields

Note that best practice in Python 2.7 is to use new-style classes (not needed with Python 3), i.e.

class Foo(object):
...

Also, there's a difference between an 'object' and a 'class'. To build a dictionary from an arbitrary object, it's sufficient to use __dict__. Usually, you'll declare your methods at class level and your attributes at instance level, so __dict__ should be fine. For example:

>>> class A(object):
... def __init__(self):
... self.b = 1
... self.c = 2
... def do_nothing(self):
... pass
...
>>> a = A()
>>> a.__dict__
{'c': 2, 'b': 1}

A better approach (suggested by robert in comments) is the builtin vars function:

>>> vars(a)
{'c': 2, 'b': 1}

Alternatively, depending on what you want to do, it might be nice to inherit from dict. Then your class is already a dictionary, and if you want you can override getattr and/or setattr to call through and set the dict. For example:

class Foo(dict):
def __init__(self):
pass
def __getattr__(self, attr):
return self[attr]

# etc...


Related Topics



Leave a reply



Submit