C# 使用CSVHelper将复杂对象列表写入CSV文件
我有一个类对象列表,其中又包含另一个类对象的列表。它们看起来像这样:C# 使用CSVHelper将复杂对象列表写入CSV文件,c#,csv,dynamic,csvhelper,expandoobject,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;
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; }
}
一个重要的注意事项是,在运行时之前我不会知道List
的长度,在运行时之前我也不会知道其中ItemList
的长度。此外,在运行时之前,我不会知道每个列的名称。现在我的目标是将这些信息写入一个CSV
文件,格式如下
我觉得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);
}
}
这可能是CsvHelper为满足您的需求所能做的最好的方法
使用(var writer=newstreamwriter(“output.csv”))
{
使用(var csv=new CsvHelper.CsvWriter(writer))
{
var columns=GetColumns();
//写头
csv.WriteField(“时间戳”);
foreach(列中的var列)
{
csv.WriteField(column.ColName);
}
csv.NextRecord();
//写行
对于(int i=0;i<列[0]。ItemList.Count;i++)
{
csv.WriteField(列[0].ItemList[i].TimeStamp.ToString(“yyyy/MM/dd HH:MM:ss”);
foreach(列中的var列)
{
csv.WriteField(column.ItemList[i].Value);
}
csv.NextRecord();
}
}
}
这是一个使用动态的版本
var columns=GetColumns();
var writeList=新列表();
对于(int i=0;i<列[0]。ItemList.Count;i++)
{
var csvItem=作为IDictionary的新ExpandooObject();
csvItem.Add(“TimeStamp”,第[0]列)。ItemList[i]。TimeStamp.ToString(“yyyy/MM/dd HH:MM:ss”);
foreach(列中的var列)
{
添加(column.ColName,column.ItemList[i].Value);
}
writeList.Add(csvItem);
}
使用(var writer=newstreamwriter(“output.csv”))
{
使用(var csv=new CsvHelper.CsvWriter(writer))
{
csv.WriteRecords(writeList);
}
}
以下是使用StringBuilder处理CSV写入的方法。此代码适用于只需要将对象转换为csv的情况。但是,如果您需要使用csv作为持久性机制(无论如何,这很疯狂,请改用mongoDb或Sqlite),则必须为这些扩展方法添加更多功能
分隔符:文件类型为CSV-逗号分隔值。所以这个代码使用“,”。如果需要,您也可以通过对此代码进行一些简单的修改来创建制表符分隔符
用法:
var columns = GetColumns();
Console.WriteLine(columns.ToCsv());
//OR
columns.SaveAsCsv(@"c:\columns.csv");
Csv扩展
public static class ColumnExtensions
{
public static void SaveAsCsv(this List<Column> columns, string filePath)
{
File.WriteAllText(columns.ToCsv(), filePath);
}
public static string ToCsv(this List<Column> columns)
{
var csv = new StringBuilder();
// Write as an expression or simply
//csv.AppendCsvHeader(nameof(Item.TimeStamp));
csv.AppendCsvHeader<string, DateTime>(x => columns.First().ItemList.First().TimeStamp);
for (var index = 0; index < columns.Count; index++)
{
var column = columns[index];
// Most csv readers don't care if you have a "," at the end of the line. But for completeness we avoid doing that.
// It makes the code a bit more complicated though. You can ignore this you want.
csv.AppendCsvHeader(column.ColName, index == columns.Count - 1);
}
csv.AppendLine();
for (var i = 0; i < columns[0].ItemList.Count; i++)
{
csv.AppendCsvField(columns[0]
.ItemList[i]
.TimeStamp.ToString("yyyy/MM/dd HH:mm:ss"));
for (var index = 0; index < columns.Count; index++)
{
var column = columns[index];
csv.AppendCsvField(column.ItemList[i]
.Value.ToString("N"), index == columns.Count - 1);
}
csv.AppendLine();
}
return csv.ToString();
}
}
public static class CsvExtensions
{
private const string Delimiter = ",";
private static string AsCsvFriendly(this string val)
{
return val?.Replace(",", ";") ?? string.Empty;
}
private static string AddDelimiterIfRequired(bool withoutDelimiter)
{
return withoutDelimiter ? string.Empty : Delimiter;
}
public static void AppendCsvField(this StringBuilder stringBuilder, string value, bool withoutDelimiter = false)
{
stringBuilder.Append($"{value.AsCsvFriendly()}{AddDelimiterIfRequired(withoutDelimiter)}");
}
public static void AppendCsvHeader(this StringBuilder stringBuilder, string value, bool withoutDelimiter = false)
{
stringBuilder.Append($"{value.AsCsvFriendly()}{AddDelimiterIfRequired(withoutDelimiter)}");
}
public static void AppendCsvHeader<TIn, TOut>(this StringBuilder stringBuilder, Expression<Func<TIn, TOut>> f, bool withoutDelimiter = false)
{
stringBuilder.Append($"{(f.Body as MemberExpression)?.Member.Name}{AddDelimiterIfRequired(withoutDelimiter)}");
}
}
公共静态类扩展
{
公共静态void SaveAsCsv(此列表列、字符串文件路径)
{
File.writealText(columns.ToCsv(),filePath);
}
公共静态字符串ToCsv(此列表列)
{
var csv=新的StringBuilder();
//作为一种表达或简单地写
//csv.AppendCsvHeader(名称(项目时间戳));
csv.AppendCsvHeader(x=>columns.First().ItemList.First().TimeStamp);
对于(变量索引=0;索引
为什么不使用StringBuilder
创建整个csv结构并将其另存为csv?它将比第二种方法快得多。至于csvHelper,我甚至不会为这样简单的事情添加nuget依赖项。@KosalaW你有一个示例让我开始吗?我之所以使用CSVHelper
是因为我们的项目已经在其他地方使用它进行许多其他CSV操作,所以我想继续使用它。谢谢!是的,它完成了任务,但它与我的第二个代码示例几乎相同;我想知道是否有办法避免使用这样的东西。
var columns = GetColumns();
Console.WriteLine(columns.ToCsv());
//OR
columns.SaveAsCsv(@"c:\columns.csv");
public static class ColumnExtensions
{
public static void SaveAsCsv(this List<Column> columns, string filePath)
{
File.WriteAllText(columns.ToCsv(), filePath);
}
public static string ToCsv(this List<Column> columns)
{
var csv = new StringBuilder();
// Write as an expression or simply
//csv.AppendCsvHeader(nameof(Item.TimeStamp));
csv.AppendCsvHeader<string, DateTime>(x => columns.First().ItemList.First().TimeStamp);
for (var index = 0; index < columns.Count; index++)
{
var column = columns[index];
// Most csv readers don't care if you have a "," at the end of the line. But for completeness we avoid doing that.
// It makes the code a bit more complicated though. You can ignore this you want.
csv.AppendCsvHeader(column.ColName, index == columns.Count - 1);
}
csv.AppendLine();
for (var i = 0; i < columns[0].ItemList.Count; i++)
{
csv.AppendCsvField(columns[0]
.ItemList[i]
.TimeStamp.ToString("yyyy/MM/dd HH:mm:ss"));
for (var index = 0; index < columns.Count; index++)
{
var column = columns[index];
csv.AppendCsvField(column.ItemList[i]
.Value.ToString("N"), index == columns.Count - 1);
}
csv.AppendLine();
}
return csv.ToString();
}
}
public static class CsvExtensions
{
private const string Delimiter = ",";
private static string AsCsvFriendly(this string val)
{
return val?.Replace(",", ";") ?? string.Empty;
}
private static string AddDelimiterIfRequired(bool withoutDelimiter)
{
return withoutDelimiter ? string.Empty : Delimiter;
}
public static void AppendCsvField(this StringBuilder stringBuilder, string value, bool withoutDelimiter = false)
{
stringBuilder.Append($"{value.AsCsvFriendly()}{AddDelimiterIfRequired(withoutDelimiter)}");
}
public static void AppendCsvHeader(this StringBuilder stringBuilder, string value, bool withoutDelimiter = false)
{
stringBuilder.Append($"{value.AsCsvFriendly()}{AddDelimiterIfRequired(withoutDelimiter)}");
}
public static void AppendCsvHeader<TIn, TOut>(this StringBuilder stringBuilder, Expression<Func<TIn, TOut>> f, bool withoutDelimiter = false)
{
stringBuilder.Append($"{(f.Body as MemberExpression)?.Member.Name}{AddDelimiterIfRequired(withoutDelimiter)}");
}
}