Search code examples
csvcsvhelper

Reading repetitive sub-sections of a CSV line / record?


In CSVHelper, is it possible to read repetetive sub-records from a single line of the source CSV file?

The CSV format I am working with has sections like the following:

(All this text would be a single line of the CSV file)

TYPICAL/EXTREME PERIODS,6,
Summer - Week Nearest Max Temperature For Period,Extreme,7/13,7/19,
Summer - Week Nearest Average Temperature For Period,Typical,6/22,6/28,
Winter - Week Nearest Min Temperature For Period,
Extreme,1/20,1/26,Winter - Week Nearest Average Temperature For Period,Typical,12/ 8,12/14,
Autumn - Week Nearest Average Temperature For Period,Typical,10/ 6,10/12,
Spring - Week Nearest Average Temperature For Period,Typical,4/26,5/ 2

The first field TYPICAL... is just a constant marker.

The second field 6 indicates how many repeating sub-records there will be.

Thus the following 6 blocks of fields each have the same format, 4 fields each. I would like to read / map each of these 6 sections to an individual class object, each of the same type.


I can see in the documentation that there are many ways to parse the data, but none quite seem to map to this. It is possible to read all the fields on a given line / record as an enumerable list of values (or a dynamically sized dynamic object) but these approaches lose most of the benefit of CSVHelper, namely that it can map the data to an object for you.


Solution

  • Unfortunately, I don't believe there is a good way to use all the mapping features of CsvHelper for your example, but it would be fairly easy to build the objects manually.

    public class Program
    {
        public static void Main(string[] args)
        {
            using (MemoryStream stream = new MemoryStream())
            using (StreamWriter writer = new StreamWriter(stream))
            using (StreamReader reader = new StreamReader(stream))
            using (CsvReader csv = new CsvReader(reader, CultureInfo.InvariantCulture))
            {
                writer.WriteLine("TYPICAL/EXTREME PERIODS,6,Summer - Week Nearest Max Temperature For Period,Extreme,7/13,7/19,Summer - Week Nearest Average Temperature For Period,Typical,6/22,6/28,Winter - Week Nearest Min Temperature For Period,Extreme,1/20,1/26,Winter - Week Nearest Average Temperature For Period,Typical,12/ 8,12/14,Autumn - Week Nearest Average Temperature For Period,Typical,10/ 6,10/12,Spring - Week Nearest Average Temperature For Period,Typical,4/26,5/ 2");
                writer.Flush();
                stream.Position = 0;
    
                csv.Configuration.HasHeaderRecord = false;
    
                csv.Read();
    
                List<Foo> result = new List<Foo>();
                var rows = csv.GetField<int>(1);
                var index = 2;
    
                for (int i = 0; i < rows; i++)
                {
                    var foo = new Foo
                    {
                        Description = csv.GetField<string>(index),
                        Type = csv.GetField<string>(index + 1),
                        Start = csv.GetField<string>(index + 2),
                        End = csv.GetField<string>(index + 3)
                    };
    
                    result.Add(foo);
                    index += 4;
                }
            }
        }
    }
    
    public class Foo
    {
        public string Description { get; set; }
        public string Type { get; set; }
        public string Start { get; set; }
        public string End { get; set; }
    }