Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/batch-file/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用CSVHelper将复杂对象列表写入CSV文件_C#_Csv_Dynamic_Csvhelper_Expandoobject - Fatal编程技术网

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)}");
    }
}