Search code examples
c#csvhelper

Trying to parse CSV rows into multiple classes with CsvHelper


I have a CSV with a single Header row. Some of the columns of each row need to map to one class while other columns of the row need to map to a different class.

I tried this but with no success:

using(var reader = new StreamReader(@"c:\temp\myfile.csv"))
{
    using(var csv = new CsvReader(reader))
    {
       csv.Configuration.RegisterClassMap<ClientMap>();
       csv.Configuration.RegisterClassMap<BookingMap>();

       var bookings = csv.GetRecords<Booking>();
       ... //iterate over bookings and write to Console

       reader.BaseStream.Position = 0;

       var clients = csv.GetRecords<Client>();
       ... //iterate over clients and write to Console
    }
}

Classes

public class Client {
   public string Firstname {get; set;}
   public string Lastname {get; set;}
   public AgesEnum Age {get; set;}
}

public class Booking {
    public string ExternalId {get; set;}
    public string Status {get; set;}
}

Maps

public class ClientMap : ClassMap<Client> 
{
   public ClientMap()
   {
       Map(m => m.Firstname).Name("FIRSTNAM");
       Map(m => m.Lastname).Name("LASTNAME");
       Map(m => m.Age).ConvertUsing(row =>{
          var age = row.GetField<int>("AGE");

          if(age < 2)
             return AgesEnum.Baby;
          if(age < 10)
             return AgesEnum.Young;
          if(age < 40)
             return AgesEnum.Middle;
          return AgesEnum.Old;
       } );
    }
}


public BookingMap : ClassMap<Booking>
{
    public BookingMap()
    {
        Map(b => b.ExternalId).Name("SYSID");
        Map(b => b.Status);
    }
}

What happens when I run this is that the bookings get returned fine and I can iterate over them. However, I get an error on the GetRecords<Client>() line stating that

The conversion cannot be performed. Text: 'Age' MemberType: TypeConverter: 'CsvHelper.TypeConversion.Int32Converter'

If I comment out all the code relating to Bookings and only perform the GetRecords<Client>(), I get no error.

Why won't this work? Do I have to set up a whole new StreamReader?


Solution

  • The problem is you are setting the stream position back to the beginning and CsvReader thinks you already read the header when you got the bookings, so it reads the first line again as data instead of as the header. You need to manually read the header again.

    using(var reader = new StreamReader(@"c:\temp\myfile.csv"))
    {
        using(var csv = new CsvReader(reader))
        {
           csv.Configuration.RegisterClassMap<ClientMap>();
           csv.Configuration.RegisterClassMap<BookingMap>();
    
           var bookings = csv.GetRecords<Booking>();
           ... //iterate over bookings and write to Console
    
           reader.BaseStream.Position = 0;
    
           csv.Read();
           csv.ReadHeader();
    
           var clients = csv.GetRecords<Client>();
           ... //iterate over clients and write to Console
        }
    }