前言
建立WebAPI(参考: core实践笔记:WebAPI(EF+Pomelo+MySQL)Code-First Repository模式)后,想将Excel表中的原始数据整理进MySQL中,然而API后期并不会涉及到任何Excel表格方面的操作,不想在项目中引用相关的库/包。首先想到的是Excel可以方便的把表格转存为CSV文件,这样就只需要读取CSV文件并映射到实体类即可。
CsvHelper
一搜之后发现有现成的轮子可用:CsvHelper,参见:https://joshclose.github.io/CsvHelper/。例程参考:https://joshclose.github.io/CsvHelper/examples/reading/get-class-records/
CsvHelper 使用
Package(版本:27.2.1)引入到项目中后,直接使用官方例程尝试读取文件:
using (var reader = new StreamReader("path\\to\\file.csv"))using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture)){var records = csv.GetRecords<Foo>();}
Oops,出错了。。。。。
CsvHelper.HeaderValidationException
HResult=0x80131500
Message=Header with name ‘xx’[0] was not found.
If you are expecting some headers to be missing and want to ignore this validation, set the configuration HeaderValidated to null.CsvHelper.MissingFieldException
HResult=0x80131500
Message=Field with name ‘xx’ does not exist. You can ignore missing fields by setting MissingFieldFound to null.CsvHelper.TypeConversion.TypeConverterException
HResult=0x80131500
Message=The conversion cannot be performed.
Text: ‘’
MemberType: System.Double
TypeConverter: ‘CsvHelper.TypeConversion.DoubleConverter’CSV文件中有相应的字段,实体类相应的字段值却为null没开玩笑,中文字段显示是乱码
原因及解决方法
问题1和2
原因在于Excel表格中的字段和实体类中的字段并不是一一对应的,实体类中有些字段表格中是没有的,反之亦然。而CsvHelper默认是开启了Header和MissingField检测的,所以出错了。——可通过配置HeaderValidated和MissingFieldFound解决。
问题3
原因是CSV文件中对应的数值字段(例如:System.Double)值为空值,空值是不能转换为Doubler的。——要么使用Nullable的类型例如:double?,或为该字段提供默认值。
问题4
原因是CsvHelper默认开启了字段名的大小写检测,不完全匹配的字段将被视为不同的字段。——可通过配置PrepareHeaderForMatch解决,参考:csvhelper-ignore-case-for-header-names
PrepareHeaderForMatch = args => args.Header.ToLower()
问题5
乱码问题须使用System.IO中的
StreamReader(string path, Encoding encoding);
读取文件,像下面这样。Encoding.RegisterProvider是必须的,否则会报错:System.ArgumentException: '‘GB18030’ is not a supported encoding name. 参考:encoding-getencoding-cant-work-in-uwp-app
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);StreamReader(foo, Encoding.GetEncoding("GB18030"))
CsvHelper 配置
可通过使用CsvConfiguration来配置CsvReader以上解决上述问题,最终代码如下:
public static List<Foo> GetFoos(){List<Foo> foos = new List<Foo>();Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);using (var reader = new StreamReader(@"D:\Data\Foos.csv", Encoding.GetEncoding("GB18030")))using (var csv = new CsvReader(reader,new CsvConfiguration(CultureInfo.InvariantCulture) {HeaderValidated = null,MissingFieldFound = null,PrepareHeaderForMatch = args => args.Header.ToLower() })) {foos = csv.GetRecords<Foo>().ToList();}return foos;}
这样就可以了,Happy Coding …