Search code examples
c#csvhelper

CsvHelper dynamic column mapping


I am following this example to map custom column names to my class model:

CsvHelper Mapping by Name

In this particular part:

public FooMap()
{
    Map(m => m.Id).Name("ColumnA");
    Map(m => m.Name).Name("ColumnB");
}

Is it possible to use string as column name instead of hard-code it? Something like this --

public FooMap()
{
    Map("Col1").Name("ColumnA");
    Map("Col2").Name("ColumnB");
}

"Col1" and "Col2" are the property of my class model. I've tried to use reflection but it didn't work:

Map(x => typeof(MyClassModel).GetProperty("Col1")).Name("ColumnA");

Please let me know if what I am trying to achieve is possible. Some additional info -- the column mapping (source and destination) are both stored in a table.

Thanks!


Solution

  • This should allow you to use a string to map both the property name and the header name.

    void Main()
    {
        var mapping = new Dictionary<string, string>
        {
            {"Id","FooId"},
            {"Name","FooName"}
        };
        
        using (var reader = new StringReader("FooId,FooName\n1,Jordan"))
        using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
        {
            var fooMap = new DefaultClassMap<Foo>();
    
            fooMap.Map(mapping);
            
            csv.Context.RegisterClassMap(fooMap);
            
            var records = csv.GetRecords<Foo>().ToList();
        }   
    }
    
    public static class CsvHelperExtensions
    {
        public static void Map<T>(this ClassMap<T> classMap, IDictionary<string, string> csvMappings)
        {
            foreach (var mapping in csvMappings)
            {
                var property = typeof(T).GetProperty(mapping.Key);
    
                if (property == null)
                {
                    throw new ArgumentException($"Class {typeof(T).Name} does not have a property named {mapping.Key}");
                }
    
                classMap.Map(typeof(T), property).Name(mapping.Value);
            }
        }
    }
    
    public class Foo
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }