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>();