Как написать только выбранные поля классов в CSV с помощью CsvHelper?

Я использую CsvHelper для чтения и записи CSV файлов, и это здорово, но я не понимаю, как писать только выбранные поля типа.

Скажем, у нас было:

using CsvHelper.Configuration;

namespace Project
{
    public class DataView
    {
        [CsvField(Name = "N")]
        public string ElementId { get; private set; }

        [CsvField(Name = "Quantity")]
        public double ResultQuantity { get; private set; }

        public DataView(string id, double result)
        {
            ElementId = id;
            ResultQuantity = result;
        }
    }
}

и мы хотели исключить "количество" CsvField из результирующего CSV файла, который мы в настоящее время генерируем, например:

using (var myStream = saveFileDialog1.OpenFile())
{
    using (var writer = new CsvWriter(new StreamWriter(myStream)))
    {
        writer.Configuration.Delimiter = '\t';
        writer.WriteHeader(typeof(ResultView));
        _researchResults.ForEach(writer.WriteRecord);
    }
}

Что я могу использовать для динамического исключения поля типа из CSV?

Если это необходимо, мы можем обработать полученный файл, но я не знаю, как удалить весь столбец CSV с помощью CsvHelper.

Ответ 1

Вы можете сделать это:

using (var myStream = saveFileDialog1.OpenFile())
{
    using (var writer = new CsvWriter(new StreamWriter(myStream)))
    {
        writer.Configuration.AttributeMapping(typeof(DataView)); // Creates the CSV property mapping
        writer.Configuration.Properties.RemoveAt(1); // Removes the property at the position 1
        writer.Configuration.Delimiter = "\t";
        writer.WriteHeader(typeof(DataView));
        _researchResults.ForEach(writer.WriteRecord);
    }
}

Мы заставляем создавать сопоставление атрибутов, а затем модифицируем его, динамически удаляя столбец.

Ответ 2

Недавно мне нужно было добиться аналогичного результата, определив, какие поля включать во время выполнения. Это был мой подход:

  • Создайте файл сопоставления для сопоставления полей, которые мне нужны во время выполнения, передав enum в конструктор класса

    public sealed class MyClassMap : CsvClassMap<MyClass>
    {
        public MyClassMap(ClassType type)
        {
            switch (type)
            {
                case ClassType.TypeOdd
                    Map(m => m.Field1);
                    Map(m => m.Field3);
                    Map(m => m.Field5);                 
                    break;
                case ClassType.TypeEven:
                    Map(m => m.Field2);
                    Map(m => m.Field4);
                    Map(m => m.Field6);                 
                    break;
                case ClassType.TypeAll:
                    Map(m => m.Field1);
                    Map(m => m.Field2);
                    Map(m => m.Field3);
                    Map(m => m.Field4);
                    Map(m => m.Field5);
                    Map(m => m.Field6);                 
                    break;
            }
        }
    }
    
  • Запишите записи на использование конфигурации отображения

    using (var memoryStream = new MemoryStream())
    using (var streamWriter = new StreamWriter(memoryStream))
    using (var csvWriter = new CsvWriter(streamWriter))
    {
        csvWriter.Configuration.RegisterClassMap(new MyClassMap(ClassType.TypeOdd));
        csvWriter.WriteRecords(records);
        streamWriter.Flush();
        return memoryStream.ToArray();
    }
    

Ответ 3

Отметьте поле следующим образом:

[CsvField( Ignore = true )]
public double ResultQuantity { get; private set; }

Обновление: Nevermind. Я вижу, вы хотите сделать это во время выполнения, а не компилировать время. Я оставлю это красным флагом для всех, кто может совершить ту же ошибку.

Ответ 4

У меня была похожая проблема с моим кодом, и я исправил ее с помощью следующего кода.

Вы можете сделать это:

var ignoreQuantity = true;
using (var myStream = saveFileDialog1.OpenFile())
{
    using (var writer = new CsvWriter(new StreamWriter(myStream)))
    {
        var classMap = new DefaultClassMap<DataView>();
        classMap.AutoMap();
        classMap.Map(m => m.ResultQuantity).Ignore(ignoreQuantity)

        writer.Configuration.RegisterClassMap(classMap);

        writer.Configuration.Delimiter = "\t";
        writer.WriteHeader(typeof(DataView));
        _researchResults.ForEach(writer.WriteRecord);
    }
}

Ответ 5

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

// Nothing special here
internal class MyClassMap<T> : ClassMap<T> where T : MyRecordBaseClass
{ 
    public MyClassMap()
    {
        AutoMap();
        Map( m => m.SOME_FIELD ).Ignore();
    }
} 

Эта часть, как правило, хорошо документирована, а не динамическая часть.

Но один класс нуждался в особом соусе, игнорируя другое поле динамически, и хотя я мог бы создать отдельный класс карты, это не масштабировалось, поскольку я ожидаю, что их будет намного больше, поэтому я наконец-то понял, как это сделать. должным образом:

    ...
    // special processing for *one* record type
    csvwriter.Configuration.RegisterClassMap<MyClassMap<ONE_RECORD_TYPE>>();

    if (ShouldIgnore)
    {
        var map = csvwriter.Configuration.Maps.Find<ONE_RECORD_TYPE>();
        map.Map( m => m.SOME_OTHER_FIELD ).Ignore();
    }
    ...

Это работало на CsvHelper версий 7.1.1 и 12.1.1.