CsvHelper - 将多列读入单个列表

问题描述 投票:0回答:5

我正在使用 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; }
}

我希望一步完成此操作,我确信我可以有一个中间步骤,将其放入具有匹配属性的对象中,但最好一次性完成此操作

c# csvhelper
5个回答
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>();
    }
}

3
投票

版本 3 支持读取和写入

IEnumerable
属性。您可以像您一样使用
IList<T>
属性。您只需要指定字段的起始索引即可。

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

2
投票

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

如果您已经有一个代表所有列的所有记录的

IEnumerable<IEnumerable<string>>
,您可以使用此 Linq 查询来获取带有
List<Person>
IList<string> Attributes

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

2
投票

您实际上不需要事先定义列名称,您可以从

FieldHeaders
属性获取它们。因此,在尼尔的答案的更动态版本中,您可以这样定义
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()
        );
    }
}

0
投票

在最新版本中,您可以在创建

Convert
时访问
ClassMap

我认为对于你的需求,它看起来像这样:

class PersonMap : ClassMap<Person>
{
    public PersonMap()
    {
        Map(m => m.FirstName).Name("FirstName");
        Map(m => m.LastName).Name("LastName");

        Map(result => result.Attributes).Convert(args => args.Row.HeaderRecord
            .Where(column => column.StartsWith("Attribute"))
            .Select(column => args.Row.GetField(column))
            .ToList());
    }
}

使用它已经有一段时间了,pure

ClassMap
绝对比大多数hacks更强大。 您需要使用 CsvHelper 上下文注册类映射。

© www.soinside.com 2019 - 2024. All rights reserved.