Home .NET - Convert/map list into object and vis versa
Reply: 3

.NET - Convert/map list into object and vis versa

adam78
1#
adam78 Published in 2017-09-14 08:17:57Z

I have the following list which contains field names and values.

public class FormField 
{
    public string FieldName { get; set;}
    public string FieldValue { get; set;}
}

var formData = new List<FormField>();
formData.Add(new FormField { FieldName = "Date", FieldValue = "2017-09-14" });
formData.Add(new FormField { FieldName = "Name", FieldValue = "Job blogs" });
formData.Add(new FormField { FieldName = "IsEnabled", FieldValue = "true" });

How can I convert or map the list into the following class? Note FieldNames map to the properties of the class.

public class MyViewModel 
{
    [Required]
    public DateTime Date { get; set; } = DateTime.now;

    [Required]
    public string Name { get; set; }

    public boolean IsEnabled { get; set; }

    public IEnumerable<SelectListItem> Titles 
    {
        get
        {
            var options = new List<SelectListItem>
            {
                new SelectListItem(){ Value = "Mr", Text = "Mr" },
                new SelectListItem(){ Value = "Mrs", Text = "Mrs" }                    
            };

            return options;
        }
    } 
}

Any help appreciated. Do I need to serialize the list somehow? Can automapper do this?

* UPDATE *

I tried the following but it doesnt work despite the automapper docs stating that you can go directly from dictionary to object:

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        CreateMap<Dictionary<string, object>, MyViewModel>();
    }
}

var viewModel = Mapper.Map<MyViewModel>(formData.ToDictionary(x => x.FieldName, x => (object) x.FieldValue))

Note: for the record I'm using automapper v 5.0.2

I also need to go back from object to dictionary but be able to exclude properties such as public IEnumerable<SelectListItem> Titles {get;}

Stephen Muecke
2#
Stephen Muecke Reply to 2017-09-14 09:01:00Z

You could use reflection to get and set the matching properties in the model

var formData = new List<FormField>();
formData.Add(new FormField { FieldName = "Date", FieldValue = "2017-09-14" });
formData.Add(new FormField { FieldName = "Name", FieldValue = "Job blogs" });
formData.Add(new FormField { FieldName = "IsEnabled", FieldValue = "true" });

// Initialize a new instance of the model
MyViewmodel model = new MyViewmodel();
// Get its property info
PropertyInfo[] properties = model.GetType().GetProperties();
// Loop through your form field data
foreach(var field in formData)
{
    // Get the matching property name
    PropertyInfo pi = properties.FirstOrDefault(x => x.Name == field.FieldName);
    if (pi == null)
    {
        continue;
    }
    // Convert the value to match the property type
    object value = Convert.ChangeType(field.FieldValue, pi.PropertyType);
    // Set the value of the property
    pi.SetValue(model, value);
}
Lucian Bargaoanu
3#
Lucian Bargaoanu Reply to 2017-09-14 15:11:53Z

AM can map by default from IDictionary<string, object>(or DynamicObject) to any destination if the property names match. So you need to map from your structure to a dictionary, LINQ probably fits best, and then to your destination. The docs and the tests.

Panagiotis Kanavos
4#
Panagiotis Kanavos Reply to 2017-09-14 09:49:35Z

It seems like you are trying to create an object that contains a dynamic number of fields, like ViewBag. You don't need mapping for this. That's already provided by .NET, through the ExpandoObject, DynamicObject classes and dynamic keyword.

Instead of building a list of fields and values, create an ExpandoObject and add fields to it, just as you would with ViewBag :

dynamic formData=new ExpandoObject();
formData.Name = "Job blogs";
formData.Date = DateTime.Today;
formData.IsEnabled = true;
formData.Titles = new []{ 
                           new SelectedListeItem{Text="Mr",Value="Mr"},
                           new SelectedListeItem{Text="Mrs",Value="Mrs"}
                        };

You can use that object as your ViewModel, just like ViewBag.

// Controller 

public ActionResult Index(..)
{
    ....
    View(formData);
}


//View 
@model dynamic 

<h1>@Model.Name</h1>

UPDATE - Expando from field list

ExpandoObject implements IDictionary<string, object> explicitly, which means that one can properties without knowing their number or names at runtime, eg:

var fields = new (string name,object value) [] 
             {
                        ("Name","Job blogs"),
                        ("Date", DateTime.Today),
                        ("IsEnabled",true)
             };

dynamic viewModel=new ExpandoObject();
var dict=(IDictionary<string,object>)viewModel;
foreach(var field in fields)
{
    dict.Add(field.name,field.value);
}

I'm using tuple syntaxt just to avoid typing FormField repeatedly

UPDATE 2 - Strongly typed class with dictionary storage

As Stephen Muecke commented, it's harder to do binding and validation with a dynamic class. On the other hand, if the fields are known in advance, why use mapping or reflection at all?

One could create a ViewModel class that accepts some fileds, converts them to a dictionary (similar to what ExpandoObject would do) and use the dictionary as the backing store for the properties.

With a bit of C# magic like the CallMemberName attribute, the extra code is minimal. There is a runtime penalty for dictionary lookups, which only becomes apparent if there are a lot of reads/writes :

class MyViewModel
{
    Dictionary<string,object> _dict=new Dictionary<string,object>();

    //Get helper
    private T getter<T>([CallerMemberName]string name=null)
    {
        return _dict.TryGetValue(name,out object value)
            ? (T)Convert.ChangeType(value,typeof(T))
            : default(T);            
    }

    private void setter(object value,[CallerMemberName]string name=null)
    {
        _dict[name]=value;
    }

    public DateTime Date { 
        get => getter<DateTime>();            
        set => setter(value); 
    }               
    public string Name { 
        get => getter<string>();            
        set => setter(value); 
    }
    public bool IsEnabled { 
        get => getter<bool>();            
        set => setter(value); 
    }

    public MyViewModel(IEnumerable<FormField> fields)
    {
        _dict=fields.ToDictionary(
                         field=>field.FieldName,
                         field=>(object)field.FieldValue);
    }

}

....

var formData = new [] {
    new FormField { FieldName = "Date", FieldValue = "2017-09-14" },
    new FormField { FieldName = "Name", FieldValue = "Job blogs" },
    new FormField { FieldName = "IsEnabled", FieldValue = "true" }
};

var myViewModel = new MyViewModel(formData);

The setter for each property just sets a dictionary value using the property's name as the key. The getter uses CallerMemberName to get the property's name and use it as the key

You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.44612 second(s) , Gzip On .

© 2016 Powered by cudou.com design MATCHINFO