加载到数据表时 NullValues 选项不起作用

Dry*_*ong 1 .net c# csvhelper

将 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.

如果我手动更改数据并更换0false1true,然后我布尔值的工作,但我得到一个类似的错误: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 布局时创建新的对象模型。

Iva*_*oev 6

在我看来,CsvDataReader阶级是无用的-实行GetFieldType回报typeof(string)GetValue也返回stringS,所以虽然它实现访问方法的类型的数据,它们永远不会被调用的DataTableLoad方法。

因此不会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默认类型转换器处理。