As stated in the title, when I replace a default converter on CSVReader.Configuration.TypeConverterCache it throws a TypeConverterException that says it used the default converter instead of the replacement. Defining the converter for that field in a ClassMap uses the replacement converter successfully.
Below is part of my application code that reproduces problem. The .csv file that I use has some unnecessary lines before the header so those are ignored but I have included them in the example.
class Program {
static void Main(string[] args) {
List<Item> items = new List<Item>();
var s = new StringBuilder();
s.Append("WORD,,\r\n");
s.Append(",地区版本备注,是否测试道具\r\n");
s.Append("ID,Party,IsActive\r\n");
s.Append("1,1,0\r\n");
s.Append("2,1,1\r\n");
s.Append("3,1,,\r\n");
Console.WriteLine("Reading with replaced default converter.");
using (var reader = new StringReader(s.ToString())) {
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture)) {
csv.Configuration.Delimiter = ",";
csv.Configuration.RegisterClassMap<ItemMapWithConverter>();
csv.Configuration.TypeConverterCache.RemoveConverter<bool>();
csv.Configuration.TypeConverterCache.AddConverter<bool>(new BooleanConverter());
Console.WriteLine($"Configuration boolean converter type :: {csv.Configuration.TypeConverterCache.GetConverter<bool>()}");
csv.Configuration.ShouldSkipRecord = ShouldSkipRecord;
bool foundHeader = false;
while (csv.Read()) {
try {
if (csv.Context.Record[0].StartsWith("ID")) {
csv.ReadHeader();
foundHeader = true;
continue;
}
if (foundHeader)
items.Add(csv.GetRecord<Item>());
}
catch (TypeConverterException ex) {
Console.WriteLine($"Exception :: {ex.Message}");
Console.WriteLine($"RawRecord :: {ex.ReadingContext.RawRecord}");
continue;
}
}
}
}
Console.WriteLine();
Console.WriteLine("Reading with converter defined in ClassMap.");
using (var reader = new StringReader(s.ToString())) {
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture)) {
csv.Configuration.Delimiter = ",";
csv.Configuration.RegisterClassMap<ItemMapWithoutConverter>();
Console.WriteLine($"Configuration boolean converter type :: {csv.Configuration.TypeConverterCache.GetConverter<bool>()}");
csv.Configuration.ShouldSkipRecord = ShouldSkipRecord;
bool foundHeader = false;
while (csv.Read()) {
try {
if (csv.Context.Record[0].StartsWith("ID")) {
csv.ReadHeader();
foundHeader = true;
continue;
}
if (foundHeader)
items.Add(csv.GetRecord<Item>());
}
catch (TypeConverterException ex) {
Console.WriteLine($"Exception :: {ex.Message}");
Console.WriteLine($"RawRecord :: {ex.ReadingContext.RawRecord}");
continue;
}
}
}
}
Console.ReadKey();
}
private static bool ShouldSkipRecord(string[] fields) {
if (string.IsNullOrEmpty(fields[0]))
return true;
return false;
}
}
public class Item{
public int Id { get; set; }
public int Class { get; set; }
public bool IsActive { get; set; }
public Item() { }
}
public class ItemMapWithConverter : ClassMap<Item> {
public ItemMapWithConverter() {
Map(m => m.Id).Name("ID");
Map(m => m.Class).Name("Party");
Map(m => m.IsActive).Name("IsActive");
}
}
public class ItemMapWithoutConverter : ClassMap<Item> {
public ItemMapWithoutConverter() {
Map(m => m.Id).Name("ID");
Map(m => m.Class).Name("Party");
Map(m => m.IsActive).Name("IsActive").TypeConverter<BooleanConverter>();
}
}
public class BooleanConverter : DefaultTypeConverter {
public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData) {
if (string.IsNullOrEmpty(text))
return false;
else
return Convert.ToBoolean(Convert.ToInt32(text));
}
public override string ConvertToString(object value, IWriterRow row, MemberMapData memberMapData) {
if ((bool)value)
return "1";
else
return "0";
}
}
So, am I doing something wrong when defining default converters or is this a bug?
There likely needs to be better documentation. You need to add your custom Converter
before registering the ClassMap
. Also, you don't need to remove the default bool converter first.
csv.Configuration.Delimiter = ",";
csv.Configuration.TypeConverterCache.AddConverter<bool>(new BooleanConverter());
csv.Configuration.RegisterClassMap<ItemMapWithConverter>();