Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reading multiple classes from single csv file using CsvHelper

Tags:

c#

csvhelper

I've been using Josh Close' CsvHelper a bit recently to parse CSV files, I quite like the fluent api for class mapping.

I'm trying to map a csv file which contains multiple record types, the file structure is

C,Comment,Timestamp
I,Class1,Header1,Header2
D,Class1,Data1,Data2
D,Class1,Data1,Data2
...
I,Class2,Header1,Header2,Header3
D,Class2,Data1,Data2,Data3
D,Class2,Data1,Data2,Data3
...
C,Checksum

Is this something which can be handled by CsvHelper? I've writen a custom parser which basically works but all it really does is filter out the Header and Data fields for a specific class - I'd really like to be able to do something like

csv.Configuration.RegisterClassMap<Class1>();
csv.Configuration.RegisterClassMap<Class2>();

var data1 = csv.GetRecords<Class1>().ToList();             
var data2 = csv.GetRecords<Class2>().ToList();

And read the file in one pass? Is this possible or am I using the wrong parser?

Regards Dave

like image 233
David Waterworth Avatar asked Dec 24 '22 11:12

David Waterworth


1 Answers

There is a way to do this; you just have to do it manually.

  1. You manually read the csv file row by row
  2. Inspect the first column for the discriminator that will indicate that you need to map to a Class object.
  3. Inspect the second column for the class to map to.
  4. Map the entire row to that given class.

    public static void ReadMultiClassCsv()
    {
        var class1Data = new List<Class1>();
        var class2Data = new List<Class2>();
    
        using (StreamReader reader = File.OpenText(@"C:\filename.csv"))
        using (var csvReader = new CsvReader(reader))
        {
            //1.  You manually read the csv file row by row
            while (csvReader.Read())
            {
                var discriminator = csvReader.GetField<string>(0);
    
                //2.  Inspect the first column for the discriminator that will indicate that you need to map to a Class object.
                if (discriminator == "D")
                {
                    var classType = csvReader.GetField<string>(1);
    
                    //3.  Inspect the second column for the class to map to.
                    switch (classType)
                    {
                        //4.  Map the entire row to that given class.
                        case "Class1":
                            class1Data.Add(csvReader.GetRecord<Class1>());
                            break;
                        case "Class2":
                            class2Data.Add(csvReader.GetRecord<Class2>());
                            break;
                        default:
                            break;
                    }
                }
            }
        }
    }
    
like image 80
drneel Avatar answered Jan 19 '23 00:01

drneel