从包含空字符串的CsvDataReader处理DateTime字段

时间:2019-05-08 20:19:35

标签: c# datatable csvhelper

我正在尝试使用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);

在加载方法上,出现以下错误:

  

字符串''无法识别为有效的DateTime。无法将<>存储在   派生的_mdd_date列。预期的类型是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,但是它只是抛出错误。

1 个答案:

答案 0 :(得分:0)

不幸的是,看起来CsvDataReader会将所有值都视为字符串,而忽略了其他类型的TypeConverters。似乎有一个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);
    }
}