Dynamically mapping nested objects with CSVHelper

感情迁移 提交于 2019-12-12 19:06:53

问题


I'm using CSVHelper (thanks Josh Close) to read a CSV file which works great. I'm now trying to use it to map that file to some internal classes; however, the CSV's I'm mapping vary by customer but all need to map to my internal classes. I need to allow the customer to define how the CSV maps to my POCO objects.

I'm storing the customer defined mappings as Dictionary<string,int> -- ["Firstname", 20],["Lastname",21],["Address.Line1",30], ["Address.Line2",31], etc.

I have a dynamic map class that works for dynamically mapping based on a given mapping at runtime. My problem lies in dealing with reference type properties. Here is what I have so far.

POCO Classes

public class Client
{
   public int Id {get; set;}
   public string Firstname {get; set;}
   public string Lastname {get; set;}
   public Address Address {get; set;}
   ...
}

public class Address
{
   public string Line1 {get; set;}
   public string Line2 {get; set;}
   public string City {get; set;}
   ...
}

Based on a few posts that I've run across here and here, I came up with the following that uses a defined mapping to map a CSV dynamically.

Dynamic Map

public class BaseCSVMap<T> : ClassMap<T> where T : class
{
   public void CreateMap(Dictionary<string,int> mappings)
   {
      foreach(var mapping in mappings)
      {
         var propname = mapping.Key;
         var csvIndex = mapping.Value;

         var member = typeof(T).GetProperty(propname);
         Map(typeof(T), member).Index(csvIndex);
       }
    }
}

Using Dynamic Map

var id = 2;  //Customer 2
var mappings = dataContext.Mappings.Where(m => m.id = id);  //Get customer 2's map
using(var reader = File.OpenText(@"c:\temp\testfile.csv"))
{
   var csv = new CsvReader(reader);
   csv.Configuration.HasHeaderRecord = true;  //hardcoded for now
   var map = new BaseCSVMap<Client>();
   map.CreateMap(mappings);
   csv.Configuration.RegisterClassMap(map);

   var records = csv.GetRecords<Client>();
}

I added the following in my BaseCSVMap<T> class, which works great if all my reference properties are strings, but doesn't work so well when a property is something else.

var member = typeof(T).GetProperty(propname);

//New code
//Mapping would look like ["Address.Line1",78]
if(member.GetType().IsClass)
{
   string exp = $"c.{propname}";
   var p = Expression.Parameter(typeof(T), "c");
   var e = System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(new [] {p}, null, exp);
   Map((Expression<Func<T,string>>)e).Index(csvIndex);
}

I also looked for a way to take advantage of the Reference mapping that CSVHelper makes available, but was unable to figure out how to do that in a dynamic fashion.

Looking for some guidance in how to accomplish defining a dynamic map for a reference type with CSVHelper.

来源:https://stackoverflow.com/questions/53106857/dynamically-mapping-nested-objects-with-csvhelper

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!