Adding detectable Nullable values to CsvHelper
I used "ConvertUsing"...
public class RecordMap : CsvHelper.Configuration.ClassMap<Record>
{
public RecordMap()
{
AutoMap();
Map(m => m.TransactionDate).ConvertUsing( NullDateTimeParser );
Map(m => m.DepositDate).ConvertUsing( NullDateTimeParser );
}
public DateTime? NullDateTimeParser(IReaderRow row)
{
//"CurrentIndex" is a bit of a misnomer here - it's the index of the LAST GetField call so we need to +1
//https://github.com/JoshClose/CsvHelper/issues/1168
var rawValue = row.GetField(row.Context.CurrentIndex+1);
if (rawValue == "NULL")
return null;
else
return DateTime.Parse(rawValue);
}
}
I think some time in the last seven years and thirteen versions since this question was asked the options for doing this without a custom type map class expanded, e.g.:
csvReader.Context.TypeConverterOptionsCache.GetOptions<string>().NullValues.Add("NULL");
csvReader.Context.TypeConverterOptionsCache.GetOptions<DateTime?>().NullValues.AddRange(new[] { "NULL", "0" });
csvReader.Context.TypeConverterOptionsCache.GetOptions<int?>().NullValues.Add("NULL");
csvReader.Context.TypeConverterOptionsCache.GetOptions<bool>().BooleanFalseValues.Add("0");
csvReader.Context.TypeConverterOptionsCache.GetOptions<bool>().BooleanTrueValues.Add("1");
CsvHelper can absolutely handle nullable types. You do not need to roll your own TypeConverter if a blank column is considered null. For my examples I am assuming you are using user-defined fluent mappings.
The first thing you need to do is construct a CsvHelper.TypeConverter
object for your Nullable types. Note that I'm going to use int
since strings allow null values by default.
public class MyClassMap : CsvClassMap<MyClass>
{
public override CreateMap()
{
CsvHelper.TypeConversion.NullableConverter intNullableConverter = new CsvHelper.TypeConversion.NullableConverter(typeof(int?));
Map(m => m.number).Index(2).TypeConverter(intNullableConverter);
}
}
Next is setting the attribute on your CsvReader object to allow blank columns & auto-trim your fields. Personally like to do this by creating a CsvConfiguration
object with all of my settings prior to constructing my CsvReader
object.
CsvConfiguration csvConfig = new CsvConfiguration();
csvConfig.RegisterClassMap<MyClassMap>();
csvConfig.WillThrowOnMissingField = false;
csvConfig.TrimFields = true;
Then you can call myReader = new CsvReader(stream, csvConfig)
to build the CsvReader
object.
IF you need to have defined values for null such as "NA" == null
then you will need to roll your own CsvHelper.TypeConversion
class. I recommend that you extend the NullableConverter
class to do this and override both the constructor and ConvertFromString
method. Using blank values as null is really your best bet though.