C# 加载到DataTable时NullValues选项不起作用
在将CSV读入数据表时,我试图添加布尔值和空值的选项,这些选项似乎不起作用。例如,包含以下类似数据的文件:C# 加载到DataTable时NullValues选项不起作用,c#,.net,csvhelper,C#,.net,Csvhelper,在将CSV读入数据表时,我试图添加布尔值和空值的选项,这些选项似乎不起作用。例如,包含以下类似数据的文件: 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 = ne
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);
}
}
在这种情况下,架构中的属性将与CSV文件中的列相关。理论上,这将允许我在运行时解析文件并验证数据类型,而不是每次引入新的CSV布局时都必须创建新的对象模型。From
如果要指定列和列类型,数据表将加载自动转换的类型
我看到的是,在使用CsvDataReader时忽略CsvReader类型转换器选项
但是如果您使用csv.GetRecords,它将使用定义的类型转换器选项
List<csvData> result = csv.GetRecords<csvData>().ToList();
从
如果要指定列和列类型,数据表将加载自动转换的类型
我看到的是,在使用CsvDataReader时忽略CsvReader类型转换器选项
但是如果您使用csv.GetRecords,它将使用定义的类型转换器选项
List<csvData> result = csv.GetRecords<csvData>().ToList();
[第二次尝试]
只要数据列集合是由CsvDataReader和配置创建的,我就可以通过CsvDataReader将数据加载到DataTable对象中。分隔符设置为逗号,但。。。布尔字段活动不是真正的布尔值
根据我的测试和对文档的理解,只有一种方法可以获得正确的数据——通过helper类,它需要设置为fields。其中两项非常重要:
BooleanFalseValuesAttribute用于表示
转换时布尔值为false。
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);
}
}
见:
如果我很了解您,您希望将数据提取到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();
[第二次尝试]
只要数据列集合是由CsvDataReader和配置创建的,我就可以通过CsvDataReader将数据加载到DataTable对象中。分隔符设置为逗号,但。。。布尔字段活动不是真正的布尔值
根据我的测试和对文档的理解,只有一种方法可以获得正确的数据——通过helper类,它需要设置为fields。其中两项非常重要:
BooleanFalseValuesAttribute用于表示
转换时布尔值为false。
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);
}
}
见:
如果我很了解您,您希望将数据提取到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();
在我看来,CsvDataReader类是无用的——GetFieldType的实现返回TypeOfsString,GetValue也返回字符串,因此尽管它实现了类型化数据访问器方法,但DataTable类加载方法从未调用它们
因此,不会发生CsvHelper映射-转换由DataTable使用标准字符串到类型转换器完成
我建议取消CsvDataReader类的使用,并替换dt.Loaddr;用这样的方式打电话:
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.GetFieldtype索引方法填充数据行值。这样,转换由CsvReader类执行,并将使用所有转换选项
顺便说一句,所显示的布尔值或空值选项都不是真正需要的-它们都由CsvHelper默认类型转换器处理。在我看来,CsvDataReader类是无用的-GetFieldType的实现返回typeofstring,GetValue也返回字符串,因此尽管它实现了类型化数据访问器方法,DataTable类装入方法从不调用它们
因此,不会发生CsvHelper映射-转换由DataTable使用标准字符串到类型转换器完成
我建议取消CsvDataReader类的使用,并替换dt.Loaddr;用这样的方式打电话:
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.GetFieldtype索引方法填充数据行值。这样,转换由CsvReader类执行,并将使用所有转换选项
顺便说一句,所显示的布尔值或空值选项都不是真正需要的-它们都由CsvHelper默认类型转换器处理。架构定义在哪里/如何定义?如果使用可为空的int版本GetOptions而不是不可为空的版本GetOptions,会发生什么情况?@grek40架构是从包含所使用CSV信息的JSON文件中解析出来的阅读当我使用nullable int选项时,我得到了e
xact相同错误您可以发布您的模式信息吗?@WAELABAS模式信息已发布,谢谢!架构定义在何处/如何定义?如果使用可为null的int版本GetOptions而不是不可为null的版本,会发生什么情况?@grek40架构是从包含所读取CSV信息的JSON文件中解析出来的。当我使用nullable int选项时,我得到了完全相同的错误。你能发布你的模式信息吗?@WaelAbbas模式信息已经发布,谢谢!若要重现此问题,您必须手动添加datatable列,并将数据类型设置为Active is boolean。@WaelAbbas,感谢您的宝贵意见。我已经更新了我的答案。请看一看。这个答案很好,但我忽略了一个重要的细节,那就是我不能使用预定义的对象。我正在编写的应用程序的目的是接收任何CSV文件并将其解析为数据表。我正在更新我的问题accordingly@DrydenLong,我认为,这不能用CsvDataReader完成。请阅读github论坛以了解报告的问题:以及Ivan Stoev answer。要重现该问题,您必须手动添加datatable列,并为其指定数据类型,如Active is boolean。@WaelAbbas,感谢您的宝贵意见。我已经更新了我的答案。请看一看。这个答案很好,但我忽略了一个重要的细节,那就是我不能使用预定义的对象。我正在编写的应用程序的目的是接收任何CSV文件并将其解析为数据表。我正在更新我的问题accordingly@DrydenLong,我认为,这不能用CsvDataReader完成。请阅读github论坛,了解报道的问题:以及Ivan Stoev的答案。这非常完美,比我希望的要复杂一些,但正是我需要的结果。我只是浪费了2个小时,没有吃午饭,因为CsvDataReader忽略了CsvReader的配置…@PanagiotisKanavos:这非常完美,比我希望的要复杂一点,但这正是我需要的结果。我只是浪费了2个小时,没有吃午饭,因为CsvDataReader忽略了CsvReader的配置…@PanagiotisKanavos: