Обработка полей DateTime из CsvDataReader, который содержит пустую строку - PullRequest
0 голосов
/ 08 мая 2019

Я пытаюсь загрузить данные из CSV-файла с помощью CsvHelper, чтобы создать таблицу данных с столбцами данных указанного типа.

                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. Не удалось сохранить <> в столбце output_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 голосов
/ 09 мая 2019

К сожалению, похоже, что CsvDataReader обрабатывает все значения как строки и игнорирует преобразователи типов для других типов. Похоже, что запрос функции добавляет эту возможность.

Я могу предложить обходной путь, который может работать для вас. Вы также можете проверить мой ответ здесь для другого варианта.

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);
    }
}
...