处理来自包含空字符串的 CsvDataReader 的 DateTime 字段
Handling DateTime fields from CsvDataReader that contains an empty string
我正在尝试使用 CsvHelper 从 CSV 文件中加载数据,以创建包含指定类型数据列的数据表。
var textReader = new StreamReader(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{tableName}.csv"));
var csvReader = new CsvReader(textReader);
var csvDataReader = new CsvDataReader(csvReader);
var dataTable = new DataTable();
foreach(var column in metaColumns)
{
var dataColumn = new DataColumn(column.columnName, GetPropertyType(column.dataType));
dataColumn.AllowDBNull = column.isNull;
dataTable.Columns.Add(dataColumn);
}
dataTable.Load(csvDataReader);
在加载方法中出现以下错误:
String '' was not recognized as a valid DateTime.Couldn't store <> in
derived_mdd_date Column. Expected type is DateTime.
显然,CsvHelper 正在将 CSV 文件中的列作为空字符串加载,然后在给定 DateTime 类型时,它不会将空字符串转换为空值。
经过一些研究和尝试,我添加了
csvReader.Configuration.TypeConverterOptionsCache.GetOptions<DateTime>().NullValues.Add("null");
csvReader.Configuration.TypeConverterOptionsCache.GetOptions<DateTime?>().NullValues.Add("null");
csvReader.Configuration.TypeConverterOptionsCache.GetOptions<string>().NullValues.Add("null");
csvReader.Configuration.TypeConverterCache.AddConverter<DateTime>(new DateFieldConverter());
csvReader.Configuration.TypeConverterCache.AddConverter<DateTime?>(new DateFieldConverter());
...
public class DateFieldConverter : DateTimeConverter
{
public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
{
bool result = DateTime.TryParse(text, out DateTime ret);
if (result) return ret;
return null;
}
}
仍然出现同样的错误。我在 DateFieldConverter 上放置了一个断点,它永远不会被击中,所以有些东西没有正确同步。我认为 DateTime 列的默认行为是 DateTime.MinValue 或 null 但它只是抛出错误。
不幸的是,看起来 CsvDataReader
将所有值都视为字符串并忽略其他类型的 TypeConverter。似乎有 feature request 来添加该功能。
我可以提供一个可能适合您的解决方法。您也可以查看我的 answer here 以获取其他选项。
public static void Main(string[] args)
{
using (MemoryStream stream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream))
using (StreamReader reader = new StreamReader(stream))
using (CsvReader csv = new CsvReader(reader))
{
writer.WriteLine("DateTime,DateTimeNullable");
writer.WriteLine("5/4/2019,");
writer.WriteLine(",5/5/2019");
writer.Flush();
stream.Position = 0;
csv.Configuration.TypeConverterCache.AddConverter<DateTime>(new DateFieldConverter());
csv.Configuration.TypeConverterCache.AddConverter<DateTime?>(new DateFieldNullableConverter());
var dataTable = new DataTable();
dataTable.Columns.Add("DateTime", typeof(DateTime)).AllowDBNull = false;
dataTable.Columns.Add("DateTimeNullable", typeof(DateTime)).AllowDBNull = true;
csv.Read();
csv.ReadHeader();
while (csv.Read())
{
var row = dataTable.NewRow();
foreach (DataColumn column in dataTable.Columns)
{
if (column.DataType == typeof(DateTime) && column.AllowDBNull)
{
row[column.ColumnName] = csv.GetField(typeof(DateTime?), column.ColumnName);
}
else
{
row[column.ColumnName] = csv.GetField(column.DataType, column.ColumnName);
}
}
dataTable.Rows.Add(row);
}
}
}
public class DateFieldConverter : DateTimeConverter
{
public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
{
if (text == string.Empty)
{
return DateTime.MinValue;
}
return base.ConvertFromString(text, row, memberMapData);
}
}
public class DateFieldNullableConverter : DateTimeConverter
{
public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
{
if (text == string.Empty)
{
return DBNull.Value;
}
return base.ConvertFromString(text, row, memberMapData);
}
}
我正在尝试使用 CsvHelper 从 CSV 文件中加载数据,以创建包含指定类型数据列的数据表。
var textReader = new StreamReader(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{tableName}.csv"));
var csvReader = new CsvReader(textReader);
var csvDataReader = new CsvDataReader(csvReader);
var dataTable = new DataTable();
foreach(var column in metaColumns)
{
var dataColumn = new DataColumn(column.columnName, GetPropertyType(column.dataType));
dataColumn.AllowDBNull = column.isNull;
dataTable.Columns.Add(dataColumn);
}
dataTable.Load(csvDataReader);
在加载方法中出现以下错误:
String '' was not recognized as a valid DateTime.Couldn't store <> in derived_mdd_date Column. Expected type is DateTime.
显然,CsvHelper 正在将 CSV 文件中的列作为空字符串加载,然后在给定 DateTime 类型时,它不会将空字符串转换为空值。
经过一些研究和尝试,我添加了
csvReader.Configuration.TypeConverterOptionsCache.GetOptions<DateTime>().NullValues.Add("null");
csvReader.Configuration.TypeConverterOptionsCache.GetOptions<DateTime?>().NullValues.Add("null");
csvReader.Configuration.TypeConverterOptionsCache.GetOptions<string>().NullValues.Add("null");
csvReader.Configuration.TypeConverterCache.AddConverter<DateTime>(new DateFieldConverter());
csvReader.Configuration.TypeConverterCache.AddConverter<DateTime?>(new DateFieldConverter());
...
public class DateFieldConverter : DateTimeConverter
{
public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
{
bool result = DateTime.TryParse(text, out DateTime ret);
if (result) return ret;
return null;
}
}
仍然出现同样的错误。我在 DateFieldConverter 上放置了一个断点,它永远不会被击中,所以有些东西没有正确同步。我认为 DateTime 列的默认行为是 DateTime.MinValue 或 null 但它只是抛出错误。
不幸的是,看起来 CsvDataReader
将所有值都视为字符串并忽略其他类型的 TypeConverter。似乎有 feature request 来添加该功能。
我可以提供一个可能适合您的解决方法。您也可以查看我的 answer here 以获取其他选项。
public static void Main(string[] args)
{
using (MemoryStream stream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream))
using (StreamReader reader = new StreamReader(stream))
using (CsvReader csv = new CsvReader(reader))
{
writer.WriteLine("DateTime,DateTimeNullable");
writer.WriteLine("5/4/2019,");
writer.WriteLine(",5/5/2019");
writer.Flush();
stream.Position = 0;
csv.Configuration.TypeConverterCache.AddConverter<DateTime>(new DateFieldConverter());
csv.Configuration.TypeConverterCache.AddConverter<DateTime?>(new DateFieldNullableConverter());
var dataTable = new DataTable();
dataTable.Columns.Add("DateTime", typeof(DateTime)).AllowDBNull = false;
dataTable.Columns.Add("DateTimeNullable", typeof(DateTime)).AllowDBNull = true;
csv.Read();
csv.ReadHeader();
while (csv.Read())
{
var row = dataTable.NewRow();
foreach (DataColumn column in dataTable.Columns)
{
if (column.DataType == typeof(DateTime) && column.AllowDBNull)
{
row[column.ColumnName] = csv.GetField(typeof(DateTime?), column.ColumnName);
}
else
{
row[column.ColumnName] = csv.GetField(column.DataType, column.ColumnName);
}
}
dataTable.Rows.Add(row);
}
}
}
public class DateFieldConverter : DateTimeConverter
{
public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
{
if (text == string.Empty)
{
return DateTime.MinValue;
}
return base.ConvertFromString(text, row, memberMapData);
}
}
public class DateFieldNullableConverter : DateTimeConverter
{
public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
{
if (text == string.Empty)
{
return DBNull.Value;
}
return base.ConvertFromString(text, row, memberMapData);
}
}