Question

I'm starting to use CSV Helper - an excellent little helper for your daily work - great stuff!

One item I'm struggling with right now are class maps - I have a little class

public class SimpleClass
{
    public int ID { get; set; }
    public string Name { get; set; }
    public decimal Percentage { get; set; }
    public bool IsValid { get; set; }
}

and for exporting to CSV, I would like to replace the IsValid values true with yes, False with no; for that purpose, I've created a class map:

public class SimpleClassMap : CsvClassMap<SimpleClass>
{
    public override void CreateMap()
    {
        Map(x => x.ID).Index(0);
        Map(x => x.Name).Index(1);
        Map(x => x.Percentage).Index(2);
        Map(x => x.IsValid).Index(3)
                           .TypeConverterOption(true, "yes")
                           .TypeConverterOption(false, "no");
    }
}

and now when exporting my data, I'm using that class map:

CsvConfiguration config = new CsvConfiguration { Delimiter = ";", HasHeaderRecord = false, Quote = '"' };
config.RegisterClassMap<SimpleClassMap>();

using (MemoryStream stm = new MemoryStream())
using (var streamWriter = new StreamWriter(stm))
using (var csvWriter = new CsvWriter(streamWriter, config))
{
    csvWriter.WriteRecords(list);
    streamWriter.Flush();
}

Unfortunately, when I inspect what has been written, I see that I still get True or False - not the yes or no as I had hoped.....

What am I missing here? I'm using CSV-Helper v2.5, installed from NuGet, and .NET 4.0 / Visual Studio 2010.

Was it helpful?

Solution

The boolean values overload for TypeConverterOption is used only when reading. It allows you to specify multiple values that can be used for true/false when reading. So you could do 1, "true", "TRUE", "True", "yes", etc.

Currently the only way to do it when writing is to create a custom type converter.

public class MyBooleanConverter : DefaultTypeConverter
{
    public override string ConvertToString( TypeConverterOptions options, object value )
    {
        if( value == null )
        {
            return string.Empty;
        }

        var boolValue = (bool)value;

        return boolValue ? "yes" : "no";
    }
}

You can then apply it to all booleans globally.

CsvHelper.TypeConversion.TypeConverterFactory.AddConverter<bool>( new MyBooleanConverter() );

Or apply it to a single property via the mapping.

Map( m => m.MyBoolProperty ).TypeConverter<MyBooleanConverter>();

OTHER TIPS

The accepted solution doesn't work with latest versions. I am using CsvHelper 15.0.0 and this is how it works.

public class MyBooleanConverter : DefaultTypeConverter
{
    public override string ConvertToString(object value, IWriterRow row, MemberMapData memberMapData)
    {
        if( value == null )
        {
            return string.Empty;
        }
        var boolValue = (bool)value;
        return boolValue ? "yes" : "no";
    }
}

Applying to single property:

Map(x => x.IsValid).Index(3).TypeConverter<MyBooleanConverter>();

Applying from configuration of reader/writter:

using var csvWriter = new CsvWriter(new StreamWriter(path), CultureInfo.InvariantCulture);
csvWriter.Configuration.TypeConverterCache.AddConverter<bool>(new MyBooleanConverter());
csvWriter.WriteRecords(data);

You can just use attributes for that:

    [BooleanFalseValues("N")]
    [BooleanTrueValues("Y")]
    public bool IsValid { get; init; }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top