質問

I use CsvHelper to read and write CSV files and it is great, yet I don't understand how to write only selected type fields.

Say we had:

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

and we wanted to exclude "Quantity" CsvField from resulting CSV file that we currently generate via something like:

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

What could I use to dynamically exclude a type field from the CSV?

If it is necessary we could process the resulting file, yet I do not know how to remove an entire CSV column with CsvHelper.

役に立ちましたか?

解決 3

You can do this:

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

We are forcing the creation of the attribute mapping and then modifying it, removing the column dynamically.

他のヒント

I recently needed to achieve a similar result by determining what fields to include at runtime. This was my approach:

  1. Create a mapping file to map which fields I need at runtime by passing in an enum into the class constructor

    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;
            }
        }
    }
    
  2. Write out the records to using the mapping configuration

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

Mark the field like this:

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

Update: Nevermind. I see you want to do this at runtime, rather than compile time. I'll leave this up as red flag for anyone else who might make the same mistake.

I had a similar issue with my code and I fixed it by the following code.

you can do this:

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

I had to solve this also: I have a couple dozen record types with a common base class plus a common field that has to be ignored by all of them:

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

This part is generally well documented and not the dynamic part.

But one class needed special sauce by ignoring a different field dynamically, and though I could have created a separate map class, this didn't scale for what I expect will be a lot more of these, so I finally figured out how to do it properly:

    ...
    // 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();
    }
    ...

This worked on CsvHelper versions 7.1.1 and 12.1.1.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top