当读取一个CSV到一个DataTable,我尝试添加选项,似乎没有奏效布尔和空值。例如,含有数据的文件类似于:
Id,MaxDiscount,Name,Active,AltId
1,,Foo,1,ABC123
2,10,Bar,0,DEF345
和使用模式文件下面的逻辑来动态获取页眉和我们预期的数据类型:
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);
}
}
这导致了错误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
}
]
}
在这种情况下,该模式中的Properties
将涉及到CSV文件列。这在理论上,将使我来解析文件并在运行时验证数据类型,而不是一个新的CSV布局推出的每一次创建一个新的对象模型。
在我看来,CsvDataReader
类是无用的 - GetFieldType
的实现返回typeof(string)
,GetValue
也返回string
s,所以虽然它实现了类型的数据访问方法,它们从未被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();
}
基本上制备柱类型映射和使用CsvReader.GetField(type, index)
方法用于填充DataRow
值。通过这种方式,转换由CsvReader
类执行,并会使用所有的转换选项。
顺便说一句,是真正需要的没有一个布尔值或空值的显示选项 - 所有这些都是由CsvHelper
默认类型转换器处理。
如果要指定列和列类型,数据表将被装入自动转换的类型。
我看到它使用CsvReader
时忽略CsvDataReader
型转换器选项。
但是,如果你使用csv.GetRecords
它将使用定义类型转换器选项。
List<csvData> result = csv.GetRecords<csvData>().ToList();
您需要将下面有作为类CSV文件
public class csvData
{
public int Id { get; set; }
public string MaxDiscount { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public string AltId { get; set; }
}
[第二试]
我能为DataTable
的收集是由CsvDataReader
创建和DataColumns
设置为逗号通过CsvDataReader
只要将数据加载到Configuration.Delimiter
对象,但...布尔场(Active
)是不是真的布尔值。
按我的测试,我的文档的理解,只有一个得到正确的数据的方式 - 通过辅助类,这需要设置attributes到各个领域。其中两个是非常重要的:
BooleanFalseValuesAttribute
转换时用于表示一个布尔假字符串值。BooleanTrueValuesAttribute
转换时用于表示一个布尔真字符串值。
所以,类的装饰可能看起来像:
public class MyData
{
[Name("Id")]
public int Id { get; set; }
[Name("MaxDiscount")]
public int? MaxDiscount { get; set; }
[Name("Name")]
public string Name { get; set; }
[Name("Active")]
[BooleanTrueValues("1")]
[BooleanFalseValues("0")]
public bool? Active { get; set; }
[Name("AltId")]
public string AltId { get; set; }
}
和辅助类,它映射字段:
public class MyDataMapper: ClassMap<MyData>
{
public MyDataMapper()
{
Map(m => m.Id);
Map(m => m.MaxDiscount);
Map(m => m.Name);
Map(m => m.Active);
Map(m => m.AltId);
}
}
然后我试图设置配置:
csv.Configuration.RegisterClassMap<MyDataMapper>();
能够获取数据到DataTable
通过CsvDataReader
对象,但是......没有成功:(
似乎CsvDataReader
忽略了某些原因配置(或者我没能成功地将它设置)。
每当有一个需要映射领域,文件说,抓住数据的正确方法是使用GetRecords<T>
方法:
var records = csv.GetRecords<Foo>();
如果我理解你很好,你要获取数据到DataTable对象......看看这个:
List<MyData> records = null;
using (var reader = new StreamReader(myfile))
using (var csv = new CsvReader(reader))
{
csv.Configuration.HasHeaderRecord = true;
csv.Configuration.IgnoreQuotes = false;
csv.Configuration.Delimiter = ",";
csv.Configuration.RegisterClassMap<MyDataMapper>();
records = csv.GetRecords<MyData>().ToList();
dt = records.Select(x=>dt.LoadDataRow(new object[]
{
x.Id,
x.MaxDiscount,
x.Name,
x.Active,
x.AltId
},false))
.CopyToDataTable();
dt.Dump();
结果是:
Id MaxDiscount Name Active AltId
1 null Foo True ABC123
2 10 Bar False DEF345