Search code examples
c#csvhelper

How to build a classmap at runtime for nested properties


Suppose I have data structures like this:

public class Foo
{
   public Bar A {get;set;}
   public Bar B {get;set;}
   public int C {get;set;}
}
public class Bar
{
   public int Value {get;set;}
}

and a CSV file with the contents

Column1,Column2,Column3
0,1,2
3,4,5

I would like to now map Column1 to A.Value and Column2 to B.Value and Column3 to C. I'm restricted to runtime mapping. For Column3 -> C, I can write

var type = typeof(Foo);
var customMap = Activator.CreateInstance(typeof(DefaultClassMap<>).MakeGenericType(type)) as ClassMap;
customMap.Map(type, type.GetProperty("C")).Name("Column3");
csv_reader.Context.RegisterClassMap(customMap);

How can I map columns 1 and 2?


Solution

  • Currently you can do this.

    void Main()
    {
        var s = new StringBuilder();
        s.Append("Column1,Column2,Column3\r\n");
        s.Append("0,1,2\r\n");
        s.Append("3,4,5\r\n");
        var config = new CsvConfiguration(CultureInfo.InvariantCulture)
        {
        };
        using (var reader = new StringReader(s.ToString()))
        using (var csv = new CsvReader(reader, config))
        {
            var fooType = typeof(Foo);
            var barType = typeof(Bar);
            
            var fooMapType = typeof(DefaultClassMap<>).MakeGenericType(fooType);
            var barMapType = typeof(DefaultClassMap<>).MakeGenericType(barType);
            
            var map = (ClassMap)ObjectResolver.Current.Resolve(fooMapType);
            map.Map(fooType, fooType.GetProperty("C")).Name("Column3");
            map.References(barMapType, fooType.GetProperty("A")).Data.Mapping.Map(barType, barType.GetProperty("Value")).Name("Column1");
            map.References(barMapType, fooType.GetProperty("B")).Data.Mapping.Map(barType, barType.GetProperty("Value")).Name("Column2");
    
            csv.Context.RegisterClassMap(map);
            csv.GetRecords<Foo>().ToList().Dump();
        }
    }
    
    private class Foo
    {
        public Bar A { get; set; }
        public Bar B { get; set; }
        public int C { get; set; }
    }
    
    public class Bar
    {
        public int Value { get; set; }
    }
    

    I'm looking into ways to make this easier for people that want to create maps at runtime.