使用CSVHelper将复杂对象列表写入CSV文件

时间:2019-10-01 21:32:10

标签: c# csv dynamic csvhelper expandoobject

我有一个类对象的列表,而这些对象又包含另一个类对象的列表。他们看起来像这样:

public class Column
{
    public string ColName { get; set; }
    public List<Item> ItemList { get; set; }
}

public class Item
{
    public DateTime TimeStamp { get; set; }
    public double Value { get; set; }
}

它们具有两个重要属性:

  1. 所有列中的Length中的List<Item> ItemList将是相同的。但是直到运行时,我才知道该长度是多少。
  2. 每列TimeStamp的实际值将相同。换句话说,每一列中的TimeStamp列表将是相同的。

如果您看一下我编写的用于创建Column对象列表的模拟函数,您将获得清晰的画面。这是对我的实际数据(从程序的其他位置获取的)的准确描述:

private static List<Column> GetColumns()
{
    var dt1 = DateTime.Now;
    var dt2 = dt1.AddSeconds(1);
    var dt3 = dt2.AddSeconds(1);
    var dt4 = dt3.AddSeconds(1);

    var col1 = new Column()
    {
        ColName = "ABC",
        ItemList = new List<Item>
        {
            new Item() { TimeStamp = dt1, Value = 1 },
            new Item() { TimeStamp = dt2, Value = 2 },
            new Item() { TimeStamp = dt3, Value = 3 },
            new Item() { TimeStamp = dt4, Value = 4 }
        }
    };

    var col2 = new Column()
    {
        ColName = "XYZ",
        ItemList = new List<Item>
        {
            new Item() { TimeStamp = dt1, Value = 4 },
            new Item() { TimeStamp = dt2, Value = 3 },
            new Item() { TimeStamp = dt3, Value = 2 },
            new Item() { TimeStamp = dt4, Value = 1 }
        }
    };

    var col3 = new Column()
    {
        ColName = "KLM",
        ItemList = new List<Item>
        {
            new Item() { TimeStamp = dt1, Value = 1 },
            new Item() { TimeStamp = dt2, Value = 2 },
            new Item() { TimeStamp = dt3, Value = 4 },
            new Item() { TimeStamp = dt4, Value = 8 }
        }
    };

    var list = new List<Column>
    {
        col1,
        col2,
        col3,
    };

    return list;
}

重要的一点是,直到运行时,我才知道List<Column>的长度,直到运行时,我也不知道其中ItemList的长度。此外,直到运行时,我才知道每列的名称。现在,我的目标是将这些信息写入以下格式的CSV文件中。

enter image description here

我觉得dynamic是前往这里的方式,所以我从这里开始:

var columns = GetColumns();

var timeStamps = columns.First().ItemList.Select(x => x.TimeStamp).ToList();

var writeList = new List<dynamic>();
for (int i = 0; i < timeStamps.Count; i++)
{
    dynamic csvItem = new ExpandoObject();
    csvItem.TimeStamp = timeStamps[i];
    // How to get columns?
    writeList.Add(csvItem);
}

using (var writer = new StreamWriter("output.csv"))
{
    using (var csv = new CsvHelper.CsvWriter(writer))
    {
        csv.WriteRecords(writeList);
    }
}

这是一个开始,但是由于我不知道我将拥有的列数和它们的名称,所以我不确定如何从此处继续。这里是否不使用dynamic

或者,我完全可以放弃使用CSVHelper,而从头开始编写如下内容,但这有点混乱。我要遍历两次列列表,总共要遍历三个循环。如果可能的话,我正在寻找一种更优雅的解决方案。

var columns = GetColumns();

var timeStamps = columns.First().ItemList.Select(x => x.TimeStamp).ToList();

var headers = "TimeStamp";
foreach (var col in columns)
{
    headers += "," + col.ColName;
}

using (var fs = new FileStream("output.csv", FileMode.Create, FileAccess.Write))
{
    using (var sw = new StreamWriter(fs))
    {
        sw.WriteLine(headers);
        for (int i = 0; i < timeStamps.Count; i++)
        {
            var line = timeStamps[i].ToString("yyyy/MM/dd HH:mm:ss");
            foreach (var col in columns)
            {
                line += "," + col.ItemList[i].Value;
            }
            sw.WriteLine(line);
        }
    }
}

3 个答案:

答案 0 :(得分:0)

这可能是您使用CsvHelper可以满足要求的最佳选择。

using (var writer = new StreamWriter("output.csv"))
{
    using (var csv = new CsvHelper.CsvWriter(writer))
    {
        var columns = GetColumns();

        // Write header
        csv.WriteField("TimeStamp");

        foreach (var column in columns)
        {
            csv.WriteField(column.ColName);
        }

        csv.NextRecord();

        // Write rows
        for (int i = 0; i < columns[0].ItemList.Count; i++)
        {
            csv.WriteField(columns[0].ItemList[i].TimeStamp.ToString("yyyy/MM/dd HH:mm:ss"));

            foreach (var column in columns)
            {
                csv.WriteField(column.ItemList[i].Value);
            }

            csv.NextRecord();
        }
    }
}

答案 1 :(得分:0)

这是使用dynamic的版本。

var columns = GetColumns();

var writeList = new List<dynamic>();

for (int i = 0; i < columns[0].ItemList.Count; i++)
{
    var csvItem = new ExpandoObject() as IDictionary<string, object>;

    csvItem.Add("TimeStamp", columns[0].ItemList[i].TimeStamp.ToString("yyyy/MM/dd HH:mm:ss"));

    foreach (var column in columns)
    {
        csvItem.Add(column.ColName, column.ItemList[i].Value);
    }

    writeList.Add(csvItem);
}

using (var writer = new StreamWriter("output.csv"))
{
    using (var csv = new CsvHelper.CsvWriter(writer))
    {
        csv.WriteRecords(writeList);
    }
}

答案 2 :(得分:0)

这是使用StringBuilder处理CSV写入的方法。此代码适用于只需要将对象转换为csv的情况。但是,如果您需要使用csv作为持久性机制(无论如何还是很疯狂,请改用mongoDb或Sqlite),则必须向这些扩展方法添加更多功能。

分隔符:文件类型为CSV-逗号分隔值。因此,此代码使用“,”。如果需要,也可以通过对这段代码进行一些简单的修改来创建TAB分隔符。

用法:

NeuronBase

Csv扩展;

var columns = GetColumns();

Console.WriteLine(columns.ToCsv());
//OR
columns.SaveAsCsv(@"c:\columns.csv");