CsvHelper - 将多列读入单个列表

14

我正在使用CSVHelper来读取大量数据。

我想知道是否有可能将最后的n列读入并将其转置为列表。

"Name","LastName","Attribute1","Attribute2","Attribute3"

将数据塑造成类似这样的形式

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public IList<string> Attributes { get; set; }
}

我希望一步完成,我知道可以有一个中间步骤,将其放入具有匹配属性的对象中,但一次性完成会更好。

4个回答

23

这个作为映射器的效果非常好。

public sealed class PersonMap : CsvClassMap<Person>
{
    private List<string> attributeColumns = 
        new List<string> { "Attribute1", "Attribute2", "Attribute3" };

    public override void CreateMap()
    {
        Map(m => m.FirstName).Name("FirstName").Index(0);
        Map(m => m.LastName).Name("LastName").Index(1);
        Map(m => m.Attributes).ConvertUsing(row =>
            attributeColumns
                .Select(column => row.GetField<string>(column))
                .Where(value => String.IsNullOrWhiteSpace(value) == false)
            );
    }
}

那么你只需要像这样:

using (var reader = new CsvReader(new StreamReader(filePath)))
{
    reader.Configuration.RegisterClassMap<PersonMap>();
    while (reader.Read())
    {
        var card = reader.GetRecord<Person>();
    }
}

1
我该如何从“Person”对象动态获取“attributeColumns”,而不是硬编码随机字符串? - George Findulov

3

第三版支持读写 IEnumerable 属性。您可以像以前一样使用 IList<T> 属性,只需指定字段的起始索引即可。

Map( m => m.Attributes ).Index( 2 );

我在你的其他帖子中也看到了这个版本3。我需要这个功能,但是我找不到这个版本...现在Nuget只提供了2.16.3版本。我该怎么办才能得到它? - cnom
1
在 NuGet 管理器中,勾选启用预发布版本的复选框。如果您是通过控制台进行操作,可以执行 Install-Package CsvHelper -Pre 命令。 - Josh Close
谢谢您的帮助,我已经完成了,但现在我需要一些关于如何映射IEnumerable属性的帮助或文档。在我的情况下,我尝试将可枚举的多个值连接成一个值以进行导出。 - cnom
1
3.0的文档尚未完成。暂时请查看单元测试示例。 - Josh Close
@JoshClose 由于我不知道起始索引,是否有办法像StartsWith('Attribute')一样映射此属性? - Farshan

2

实际上,您不需要预先定义列名,可以从FieldHeaders属性中获取它们。因此,在Neil答案的更动态版本中,您可以将您的Mapper定义为如下:

public sealed class PersonMap : CsvClassMap<Person> {
    public override void CreateMap() {
        Map(m => m.FirstName).Name("FirstName").Index(0);
        Map(m => m.LastName).Name("LastName").Index(1);
        Map(m => m.Attributes).ConvertUsing(row =>
            (row as CsvReader)?.FieldHeaders
                 .Where(header => header.StartsWith("Attribute"))
                 .Select(header => row.GetField<string>(header))
                 .Where(value => !string.IsNullOrWhiteSpace(value))
                 .ToList()
        );
    }
}

它不起作用。我得到了编译错误,无法通过引用转换、装箱转换、拆箱转换、包装转换或空类型转换将类型“Person”转换为“CsvHelper.CsvReader”。 - sky91
对我来说不起作用,我得到了“FieldHeaders”在属性“CsvReader”上不存在的错误。 - Sigex
@Sigex,你使用的CSVHelper版本是哪个?我之前写过这个答案,可能一些属性已经改变了。 - Dimitri Troncquo

2

我不知道这个库,所以以下内容可能有帮助也可能没有。

如果您已经有一个表示所有记录和所有列的 IEnumerable<IEnumerable<string>>,您可以使用此 Linq 查询来获取带有 IList<string> AttributesList<Person>

IEnumerable<IEnumerable<string>> allRecords = .....;
IEnumerable<Person> allPersons = allRecords
.Select(rec => 
{
    var person = new Person();
    person.FirstName = rec.ElementAt(0);
    person.LastName = rec.ElementAtOrDefault(1);
    person.Attributes = rec.Skip(2).ToList();
    return person;
}).ToList();

编辑:已下载该库,至少编译通过,无法进行真正的测试:

IList<Person> allPersons = new List<Person>();
using (var reader = new CsvHelper.CsvReader(yourTextReader))
{
    while (reader.Read())
    {
        var person = new Person();
        person.FirstName = reader.CurrentRecord.ElementAt(0);
        person.LastName = reader.CurrentRecord.ElementAtOrDefault(1);
        person.Attributes = reader.CurrentRecord.Skip(2).ToList();
        allPersons.Add(person);
    }
}

我喜欢这里使用linq的方式,我最终在库的限制下做了类似的事情。 - Neil
@Neil:我已经下载了这个库并编辑了我的答案,提供了一些可以编译的东西,可能会给你一些想法。但是,我无法测试它,现在必须去睡觉了 ;) - Tim Schmelter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接