CsvHelper - чтение нескольких столбцов в один список

Я использую 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; }
}

Я хочу сделать это за один шаг, я уверен, что у меня может быть промежуточный шаг, на котором я помещаю объект с соответствующими свойствами атрибута, но было бы неплохо сделать это на одном


person Neil    schedule 27.06.2013    source источник


Ответы (4)


Это помогает картографу.

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>();
    }
}
person Neil    schedule 28.06.2013
comment
Как я должен динамически получать attributeColumns из объекта Person вместо жесткого кодирования случайных строк? - person George Findulov; 22.02.2016

Версия 3 поддерживает чтение и запись свойств IEnumerable. Вы можете использовать свойство IList<T> так же, как и вы. Вам просто нужно указать начальный индекс поля.

Map( m => m.Attributes ).Index( 2 );
person Josh Close    schedule 25.08.2016
comment
Я видел эту версию 3 и в других ваших сообщениях. Мне нужна эта функция, но я не нахожу эту версию ... Nuget сейчас предоставляет только до версии 2.16.3. Как я могу это получить? - person cnom; 01.03.2017
comment
В диспетчере NuGet установите флажок, чтобы включить предварительные версии. Если вы делаете это через консоль, вы можете сделать Install-Package CsvHelper -Pre - person Josh Close; 01.03.2017
comment
Спасибо за это, я сделал это, но теперь я мог бы использовать некоторую помощь или документацию о том, как сопоставить свойства IEnumerable .. В моем случае я пытаюсь объединить несколько значений перечисляемого в одно значение для экспорта. - person cnom; 02.03.2017
comment
Документация для 3.0 еще не закончена. Ознакомьтесь с примерами модульных тестов на данный момент. - person Josh Close; 02.03.2017
comment
@JoshClose Поскольку я не знаю начального индекса, есть ли способ сопоставить это свойство, например StartsWith ('Attribute'). - person Farshan; 31.12.2020

Я не знаю эту библиотеку, поэтому следующее может быть полезно или нет.

Если у вас уже есть 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);
    }
}
person Tim Schmelter    schedule 27.06.2013
comment
Мне здесь нравится использование linq, в итоге я сделал что-то подобное в пределах библиотеки - person Neil; 28.06.2013
comment
@Neil: Я загрузил библиотеку и отредактировал свой ответ, чтобы предоставить то, что компилируется и может дать вам представление. Однако я не смог это проверить и должен лечь спать сейчас;) - person Tim Schmelter; 28.06.2013

На самом деле вам не нужно заранее определять имена столбцов, вы можете получить их из свойства 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()
        );
    }
}
person Dimitri Troncquo    schedule 26.04.2016
comment
Не работает. Я получил ошибку компиляции, не могу преобразовать тип 'Person' в 'CsvHelper.CsvReader' с помощью преобразования ссылки, преобразования упаковки, преобразования распаковки, преобразования упаковки или преобразования нулевого типа - person sky91; 25.04.2018
comment
У меня не работает, я понимаю, что FieldHeaders не существует в собственности CsvReader - person Sigex; 08.04.2019
comment
@Sigex, какую версию CSVHelper вы используете? Я написал этот ответ некоторое время назад, возможно, некоторые свойства изменились - person Dimitri Troncquo; 25.11.2019