Search code examples
csvhelper

Create new record inside a ClassMap ConvertUsing method


The case is that I'm parsing a file, that looks something like this.

Name Account
Foo client 123
Bar client 456,789

In the real data I do some more stuff, to clean up account, etc, but the main point is the , in the Account column. I now get an IEnumerable with 2 records, I'd like to get an IEnumerable with 3 records, by splitting records on the comma in the Account column.

Like this.

Name Account
Foo client 123
Bar client 456
Bar client 789

Is this possible to achieve through CSV Helper?


Solution

  • void Main()
    {
        using (var sReader = new StringReader("Name,Account\nFoo client,123\nBar client,\"456,789\""))
        using (var csvReader = new CsvHelper.CsvReader(sReader, CultureInfo.InvariantCulture))
        {
            csvReader.Read();
            csvReader.ReadHeader();
            
            var records = new List<Foo>();
            
            while (csvReader.Read())
            {
                var name = csvReader.GetField("Name");
                var account = csvReader.GetField("Account");
                
                var splitAccounts = account.Split(",");
                
                foreach(var item in splitAccounts)
                {
                    records.Add(new Foo { Name = name, Account = int.Parse(item)});
                }
                
            }
            records.Dump();
        }
    }
    
    public class Foo 
    {
        public string Name { get; set; }
        public int Account { get; set; }
    }
    

    Update: Each row is a record, so you really can't create a new record with your ClassMap outside of the row. What you might be able to do is wrap your class in another class that contains a List<MyClass> of the records you are trying to create and then combine them back into a single List<MyClass>. If Account can be type string in MyClass you could get away without the separate MyClassMap and just let it AutoMap() your class.

    void Main()
    {
        using (var sReader = new StringReader("Name,Account\nFoo client,123\nBar client,\"456,789\""))
        using (var csvReader = new CsvHelper.CsvReader(sReader, CultureInfo.InvariantCulture))
        {
            csvReader.Configuration.RegisterClassMap<FooMap>();
            csvReader.Configuration.RegisterClassMap<MyClassMap>();
            
            var wrappedRecords = csvReader.GetRecords<Foo>();
            
            var records = wrappedRecords.SelectMany(r => r.Clients).ToList();
            
            records.Dump();
        }
    }
    
    public class FooMap : ClassMap<Foo>
    {
        public FooMap()
        {
            Map(x => x.Clients).ConvertUsing(x =>
            {
                var records = new List<MyClass>();
                
                var record = x.GetRecord<MyClass>();
                
                var splitAccounts = x.GetField("Account").Split(",");
    
                foreach (var item in splitAccounts)
                {
                    records.Add(new MyClass { Name = record.Name, Account = int.Parse(item) });
                }
                
                return records;
            });
        }
    }
    
    public class MyClassMap : ClassMap<MyClass>
    {
        public MyClassMap()
        {
            Map(x => x.Name);
        }   
    }
    
    public class Foo
    {
        public List<MyClass> Clients { get; set; }
    }
    
    public class MyClass
    {
        public string Name { get; set; }
        public int Account { get; set; }
    }