I am currently using CSV helper to read contents of a csv file and output it to console. I have installed the csvHelper nuget package. However when I run the code I get the following error:
CsvHelper.TypeConversion.TypeConverterException: 'The conversion cannot be performed. Text: '' MemberType: TypeConverter: 'CsvHelper.TypeConversion.Int32Converter''
I understand that this is due to the fact that the field population in the csv is empty. I would currently like to be able to validate the field and set it to 0. How can I do this with CSVhelper.
My code for reading the csv is:
class ReaderCsv
{
private string _cvsfilepath;
public ReaderCsv(string csvfilepath)
{
this._cvsfilepath = csvfilepath;
}
public List <Country> ReadAllCountries()
{
var countries = new List<Country>();
using (var sr = new StreamReader(_cvsfilepath))
using (var csv = new CsvReader(sr, System.Globalization.CultureInfo.InvariantCulture))
{
csv.Configuration.Delimiter = ",";
csv.Read();
csv.ReadHeader();
while (csv.Read())
{
var country= new Country();
{
country.CountryName = csv.GetField("CountryName");
country.CountryCode = csv.GetField("CountryCode");
country.Continent = csv.GetField("CountryCode");
country.Population = csv.GetField<int>("Population");
}
countries.Add(country);
}
}return countries;
}
}
}
my mapping class is
public class CountryMap : ClassMap<Country>
{
public CountryMap()
{
Map(m => m.CountryName);
Map(m => m.CountryCode);
Map(m => m.Continent);
Map(m => m.Population);
}
}
The CSV Helper provides overloads of GetField method to which you can pass a custom type converter.
https://joshclose.github.io/CsvHelper/api/CsvHelper/CsvReader/
Therefore; and not only for Int32 but for any type, here is an implementation using a custom generic type converter that returns the default value of the type if the conversion fails.
This does not mean that you have to swallow or ignore the exception. This converter will also give you the conversion error and the offending value so that you can handle this invalid data.
I also added a lineNumber variable to track on which line the invalid data resides.
I hope this helps.
public class Defaulter<T> : CsvHelper.TypeConversion.ITypeConverter
{
Exception conversionError;
string offendingValue;
public Exception GetLastError()
{
return conversionError;
}
public string GetOffendingValue()
{
return offendingValue;
}
object CsvHelper.TypeConversion.ITypeConverter.ConvertFromString(string text, IReaderRow row, CsvHelper.Configuration.MemberMapData memberMapData)
{
conversionError = null;
offendingValue = null;
try
{
return (T)Convert.ChangeType(text, typeof(T));
}
catch (Exception localConversionError)
{
conversionError = localConversionError;
}
return default(T);
}
string CsvHelper.TypeConversion.ITypeConverter.ConvertToString(object value, IWriterRow row, CsvHelper.Configuration.MemberMapData memberMapData)
{
return Convert.ToString(value);
}
}
And here is the modified version of your code to track the line number as well as to handle the error if you want:
public class ReaderCsv
{
private string _cvsfilepath;
public ReaderCsv(string csvfilepath)
{
this._cvsfilepath = csvfilepath;
}
public List<Country> ReadAllCountries()
{
var countries = new List<Country>();
using (var sr = new StreamReader(_cvsfilepath))
using (var csv = new CsvReader(sr, System.Globalization.CultureInfo.InvariantCulture))
{
csv.Configuration.Delimiter = ",";
csv.Read();
csv.ReadHeader();
Defaulter<int> customInt32Converter = new Defaulter<int>();
int lineNumber = 0;
while (csv.Read())
{
lineNumber++;
var country = new Country();
{
country.CountryName = csv.GetField("CountryName");
country.CountryCode = csv.GetField("CountryCode");
country.Continent = csv.GetField("CountryCode");
country.Population = csv.GetField<int>("Population", customInt32Converter);
if (customInt32Converter.GetLastError() != null)
{
// The last conversion has failed.
// Handle it here.
string errorMessage = "The conversion of Population field on line " + lineNumber + " has failed. The Population value was: [" + customInt32Converter.GetOffendingValue() + "]";
}
}
countries.Add(country);
}
}
return countries;
}
}
Regards.