将 CSV 读入 DataTable 时,我试图为似乎不起作用的布尔值和空值添加选项。例如,包含类似于以下数据的文件:
Id,MaxDiscount,Name,Active,AltId
1,,Foo,1,ABC123
2,10,Bar,0,DEF345
Run Code Online (Sandbox Code Playgroud)
以及使用架构文件动态获取我们期望的标头和数据类型的以下逻辑:
var dt = new DataTable();
using (var reader = new StreamReader(file.FullName))
using (var csv = new CsvReader(reader))
{
csv.Configuration.HasHeaderRecord = true;
csv.Configuration.IgnoreQuotes = false;
csv.Configuration.TypeConverterOptionsCache.GetOptions<int>().NullValues.Add(string.Empty);
csv.Configuration.TypeConverterOptionsCache.GetOptions<bool>().BooleanFalseValues.Add("0");
csv.Configuration.TypeConverterOptionsCache.GetOptions<bool>().BooleanTrueValues.Add("1");
using (var dr = new CsvDataReader(csv))
{
foreach (var p in schema.Properties)
{
var type = Type.GetType(p.Type, true, true);
var dc = new DataColumn
{
ColumnName = p.Name,
Unique = p.IsId,
AllowDBNull = p.Nullable,
DataType = type
};
dt.Columns.Add(dc);
}
dt.Load(dr);
}
}
Run Code Online (Sandbox Code Playgroud)
这导致错误 String was not recognized as a valid Boolean. Couldn't store <0> in Active Column. Expected type is Boolean.
如果我手动更改数据并更换0同false和1同true,然后我布尔值的工作,但我得到一个类似的错误:Input string was not in a correct format. Couldn't store <> in MaxDiscount Column. Expected type is Int32.
为了让它起作用,我在这里缺少什么吗?还是类型转换器选项仅适用于已知对象?
编辑:
解析 CSV 文件时,我无法使用任何预定义的对象模型,因为它们可以包含任意数量的字段。只要模式存在,程序就应该知道如何处理它。示例架构如下所示:
{
"type": "Part",
"bucket": "s3Bucket",
"prefix": "prefix/of/datafile",
"targetDirectory": "..\\path\\to\\working\\dir",
"delimiter": ",",
"properties": [
{
"name": "Id",
"type": "System.String",
"required": true,
"nullable": false,
"isId": true,
"defaultValue": null,
"minLength": 6,
"maxLength": 8
},
{
"name": "MaxDiscount",
"type": "System.Int32",
"required": true,
"nullable": true,
"isId": false,
"defaultValue": null,
"minLength": -1,
"maxLength": -1
},
{
"name": "Name",
"type": "System.String",
"required": true,
"nullable": false,
"isId": false,
"defaultValue": null,
"minLength": 1,
"maxLength": 127
},
{
"name": "Active",
"type": "System.Boolean",
"required": true,
"nullable": false,
"isId": false,
"defaultValue": null,
"minLength": 1,
"maxLength": 1
},
{
"name": "AltId",
"type": "System.String",
"required": true,
"nullable": true,
"isId": false,
"defaultValue": null,
"minLength": 1,
"maxLength": 127
}
]
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,Properties架构中的 将与 CSV 文件中的列相关。理论上,这将允许我在运行时解析文件并验证数据类型,而不必在每次引入新的 CSV 布局时创建新的对象模型。
在我看来,CsvDataReader阶级是无用的-实行GetFieldType回报typeof(string),GetValue也返回stringS,所以虽然它实现访问方法的类型的数据,它们永远不会被调用的DataTable类Load方法。
因此不会CsvHelper发生映射 - 转换是通过DataTable使用标准字符串到类型转换器来完成的。
我建议删除CsvDataReader类的使用并用这样的dt.Load(dr);东西替换调用:
static void Load(DataTable dt, CsvReader csv)
{
if (csv.Configuration.HasHeaderRecord)
{
if (!csv.Read()) return;
csv.ReadHeader();
}
var valueTypes = new Type[dt.Columns.Count];
for (int i = 0; i < valueTypes.Length; i++)
{
var dc = dt.Columns[i];
var type = dc.DataType;
if (dc.AllowDBNull && type.IsValueType)
type = typeof(Nullable<>).MakeGenericType(type);
valueTypes[i] = type;
}
var valueBuffer = new object[valueTypes.Length];
dt.BeginLoadData();
while (csv.Read())
{
for (int i = 0; i < valueBuffer.Length; i++)
valueBuffer[i] = csv.GetField(valueTypes[i], i);
dt.LoadDataRow(valueBuffer, true);
}
dt.EndLoadData();
}
Run Code Online (Sandbox Code Playgroud)
基本上准备列类型映射并使用CsvReader.GetField(type, index)填充DataRow值的方法。这样,转换由CsvReader类执行,并将使用所有转换选项。
顺便说一句,布尔值或空值的显示选项都不是真正需要的——所有这些选项都由CsvHelper默认类型转换器处理。