使用CSVHelper导入第三方CSV遇类型转换异常排查
ToList()抛出类型转换异常 场景概述
从第三方导入CSV文件,仅需提取full_name、email、phone_number、date_of_birth四个字段,但源CSV表头包含id、created_time等多余列。已定义对应实体类和启用AutoMap的ClassMap,并配置CsvConfiguration禁用HeaderValidated和MissingFieldFound检查,但调用GetRecords<T>().ToList()时抛出异常,仅调用GetRecords<T>无异常;移除CSV中多余列或交换前两列位置后,代码可正常运行。
初始实体类与ClassMap代码
public class CSVFileVars { public string? full_name { get; set; } public string? email { get; set; } public string? phone_number { get; set; } public DateTime date_of_birth { get; set; } } public sealed class CSVFileVarsMap : ClassMap<CSVFileVars> { public CSVFileVarsMap() { AutoMap(CultureInfo.InvariantCulture); } }
导入配置代码
... StreamReader reader; List<CSVFileVars> records = new List<CSVFileVars>(); reader = new StreamReader(memStrm); var config = new CsvConfiguration(CultureInfo.InvariantCulture) { Delimiter = ",", HeaderValidated = null, MissingFieldFound = null, IgnoreBlankLines = false, HasHeaderRecord = true }; using (var csv = new CsvReader(reader, config)) { csv.Context.RegisterClassMap<CSVFileVarsMap>(); var nonlistrecords = csv.GetRecords<CSVFileVars>(); // 无异常 records = csv.GetRecords<CSVFileVars>().ToList(); // 抛出异常
更新说明
移除CSV中未使用列后可正常运行,测试用异常CSV示例:
id,created_time,ad_id,is_organic,platform,full_name,email,phone_number,date_of_birth,lead_status l:1156662815489222,2024-05-17T15:34:04+01:00,ag:120208857852060391,FALSE,fb,John Doe,jdo@gmail.com,p:+44712345,06/21/1989,complete l:1606325686856333,2024-05-17T14:35:29+01:00,ag:120208857852060391,FALSE,fb,Jane Doeless,Jdoless@ntlworld.com,p:+44234567,02/03/1952,complete
后续修改ClassMap尝试将字段设为Optional:
public sealed class CSVFileVarsMap : ClassMap<CSVFileVars> { public CSVFileVarsMap() { Map(m => m.created_time).Optional(); Map(m => m.full_name).Optional(); Map(m => m.email).Optional(); Map(m => m.phone_number).Optional(); Map(m => m.date_of_birth).Optional(); AutoMap(CultureInfo.InvariantCulture); } }
但异常仍存在:调用csv.GetRecords<CSVFileVars>()无异常,调用ToList()时抛出类型转换异常:
The conversion cannot be performed.
Text: ''
MemberName: created_time
MemberType: System.DateTime
TypeConverter: 'CsvHelper.TypeConversion.DateTimeConverter'
IReader state:
ColumnCount: 10
CurrentIndex: 1
HeaderRecord:
["id","created_time","ad_id","is_organic","platform","full_name","email","phone_number","date_of_birth","lead_status"]
IParser state:
ByteCount: 0
CharCount: 384
Row: 4
RawRow: 4
Count: 10
RawRecord:
,,,,,,,,,,
关键现象
交换CSV第一列(id)与第二列(created_time)的位置后,代码可正常运行。
排查方向
- 无效映射字段问题:修改
ClassMap时错误添加了Map(m => m.created_time),但实体类中并无该字段,导致CSVHelper尝试将空白行的created_time列(空字符串)映射到不存在的DateTime类型字段,引发转换异常。交换列位置后,空白行的对应列未被错误映射到DateTime类型,因此无异常,需移除该无效映射。 - AutoMap与手动Map顺序冲突:手动调用
Map后再执行AutoMap,可能导致映射规则混乱。正确做法是仅保留AutoMap,让CSVHelper自动映射实体类中存在的字段,忽略多余列。 - 空白行处理配置:当前设置
IgnoreBlankLines = false,CSVHelper会读取空白行并尝试映射。若需保留此配置,需确保所有映射字段能处理空值,例如给date_of_birth设置默认值:Map(m => m.date_of_birth).Optional().Default(DateTime.MinValue)。 - CSVHelper版本兼容性:部分旧版本可能存在
AutoMap与多余列处理的Bug,尝试升级到最新稳定版验证问题是否解决。
内容的提问来源于stack exchange,提问作者Brett JB

