CSVHelper:替换默认的 TypeConverter 在读取时抛出 TypeConverterException
CSVHelper: replacing a default TypeConverter throws a TypeConverterException when reading
如标题所述,当我替换 CSVReader.Configuration.TypeConverterCache 上的默认转换器时,它抛出一个 TypeConverterException,表明它使用默认转换器而不是替换。在 ClassMap 中为该字段定义转换器成功使用替换转换器。
下面是重现问题的部分应用程序代码。我使用的 .csv 文件在 header 之前有一些不必要的行,因此这些行被忽略,但我已将它们包含在示例中。
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";
}
}
所以,我是在定义默认转换器时做错了什么还是这是一个错误?
可能需要更好的文档。您需要在注册 ClassMap
之前添加您的自定义 Converter
。另外,您不需要先删除默认的 bool 转换器。
csv.Configuration.Delimiter = ",";
csv.Configuration.TypeConverterCache.AddConverter<bool>(new BooleanConverter());
csv.Configuration.RegisterClassMap<ItemMapWithConverter>();
如标题所述,当我替换 CSVReader.Configuration.TypeConverterCache 上的默认转换器时,它抛出一个 TypeConverterException,表明它使用默认转换器而不是替换。在 ClassMap 中为该字段定义转换器成功使用替换转换器。
下面是重现问题的部分应用程序代码。我使用的 .csv 文件在 header 之前有一些不必要的行,因此这些行被忽略,但我已将它们包含在示例中。
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";
}
}
所以,我是在定义默认转换器时做错了什么还是这是一个错误?
可能需要更好的文档。您需要在注册 ClassMap
之前添加您的自定义 Converter
。另外,您不需要先删除默认的 bool 转换器。
csv.Configuration.Delimiter = ",";
csv.Configuration.TypeConverterCache.AddConverter<bool>(new BooleanConverter());
csv.Configuration.RegisterClassMap<ItemMapWithConverter>();