Search code examples
c#csvhelper

Write CSV from Nested anonymous object


I didn't succeed to write a CSV for the following structure, I'm pretty sure it should be possible automatically but I'm missing something...

The _recordedData variable is a dictionary of List<RecordSample> such as the data property of the RecordedSample for a specific key of the dictionary will always be the same.

For example, _recordedData["eulerAngle"] would return a List<RecordSample> where the data property of each RecordSample is an EulerAnglesMessage struct.

But when I'm trying to export each List<RecordSample> of the dictionary in the ExportRecordedData() method, the resulting csv file doesn't contain the property of the data struct.

I don't know what I'm missing to make it work...

Thanks a lot for your help !

private static Dictionary<string, List<RecordSample>> _recordedData = new Dictionary<string, List<RecordSample>>();

private class RecordSample
{
    public double record_time_ms { get; set; } // The elapsed time in ms since the beginning of the recording
    public string record_id; // A string identifier to identify the sensors device at the origin of the record
    public object data { get; set; } // The data being recorded
}

public struct EulerAnglesMessage
{
    public UInt64 timestamp { get; set; }
    public float roll { get; set; }
    public float pitch { get; set; }
    public float yaw { get; set; }
}

public struct QuaternionMessage
{
    public UInt64 timestamp { get; set; }
    public float w { get; set; }
    public float x { get; set; }
    public float y { get; set; }
    public float z { get; set; }
}

private static void ExportRecordedData()
{
    foreach (string key in _recordedData.Keys)
    {
        string path = $"{export_path}\\{key}.csv";
        using (var writer = new StreamWriter(path))
        {
            using (CsvWriter csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
            {
                 csv.WriteRecords(_recordedData[key]);
             }
        }
    }
}

Solution

  • To do it using only CsvWriter, without modifying classes or other, you need to manually write headers and each row. But it is not that hard, as you can see in below code example:

    using CsvHelper;
    using System.Globalization;
    
    const string export_path = "test.csv";
    
    var data = new List<RecordData>();
    data.Add(new RecordData());
    data.Add(new RecordData());
    data.Add(new RecordData());
    
    using (var writer = new StreamWriter(export_path))
    {
        using (CsvWriter csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
        {
            // First write header
            csv.WriteHeader(data[0].GetType());
            csv.WriteHeader(data[0].Value.GetType());
            csv.NextRecord();
            // Then write each row
            foreach (var item in data)
            {
                csv.WriteRecord(item);
                csv.WriteRecord(item.Value);
    
                // Remember to start new record at the end!
                csv.NextRecord();
            }
        }
    }
    
    public class RecordData
    {
        public double Time { get; set; } = 42;
        public string Id { get; set; } = Guid.NewGuid().ToString();
        public object Value { get; set; } = new Data();
    }
    
    public class Data
    {
        public string Prop1 { get; set; } = Guid.NewGuid().ToString();
        public string Prop2 { get; set; } = Guid.NewGuid().ToString();
    }