CsvHelper неправильно анализирует

У меня есть следующий CSV.

Org Defined ID,Username,FirstName,LastName,Attempt #,Attempt Start,Attempt End,Section #,Q #,Q Type,Q Title,Q Text,Bonus?,Difficulty,Answer,Answer Match,Score,Out Of
,testomalley,Test,O'Malley,1,2/3/2016 15:24,2/3/2016 15:28,,1,LA,Q(1) 1- 5 Part 1,"Scenario 1 for Questions 1 through 5. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.  Part 1 - Sed ut perspiciatis unde omnis iste natus error sit voluptatem.",FALSE,1,Here is my answer o grader. Isn't it brilliant?,,0,2

Теперь вот мой объект, в который я пытаюсь его преобразовать:

public class Exam
{
    public int? OD_ID { get; set; }
    public string Username { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int AttemptNo { get; set; }
    public DateTime AttemptStart { get; set; }
    public DateTime AttemptEnd { get; set; }
    public int? SectionNo { get; set; }
    public int QuestionNo { get; set; }
    public string QuestionType { get; set; }
    public string QuestionTitle { get; set; }
    public string Questiontext { get; set; }
    public string Bonus { get; set; }
    public int Difficulty { get; set; }
    public string Answer { get; set; }
    public string AnswerMatch { get; set; }
    public int Score { get; set; }
    public int OutOf { get; set; }
}

У меня определена эта карта:

public sealed class ExamMap : CsvHelper.Configuration.CsvClassMap<Exam>
{
    public ExamMap()
    {
        Map(m => m.OD_ID).Name("Org Defined ID");
        Map(m => m.Username).Name("Username");
        Map(m => m.FirstName).Name("FirstName");
        Map(m => m.LastName).Name("LastName");
        Map(m => m.AttemptNo).Name("Attempt #");
        Map(m => m.AttemptStart).Name("Attempt Start");
        Map(m => m.AttemptEnd).Name("Attempt End");
        Map(m => m.SectionNo).Name("Section #");
        Map(m => m.QuestionNo).Name("Q #");
        Map(m => m.QuestionType).Name("Q Type");
        Map(m => m.QuestionTitle).Name("Q Title");
        Map(m => m.Questiontext).Name("Q Text");
        Map(m => m.Bonus).Name("Bonus?").TypeConverterOption(true, "TRUE").TypeConverterOption(false, "FALSE");
        Map(m => m.Difficulty).Name("Difficulty");
        Map(m => m.Answer).Name("Answer");
        Map(m => m.AnswerMatch).Name("Answer Match");
        Map(m => m.Score).Name("Score");
        Map(m => m.OutOf).Name("Out Of");
    }
}

и этот параметр TypeConversion для моего поля DateTime:

var options = new CsvHelper.TypeConversion.TypeConverterOptions
{
    //        2/3/2016 15:24
    Format = "MM/dd/yyyy HH:nn", // also tried "g" with no success.
};

Это код для анализа файла CSV:

using (StreamReader sr = new StreamReader(fileName))
{
    using (CsvReader csvread = new CsvReader(sr))
    {
        csvread.Configuration.WillThrowOnMissingField = false;
        csvread.Configuration.HasHeaderRecord = true;
        CsvHelper.TypeConversion.TypeConverterOptionsFactory.AddOptions<DateTime>(options);

        List<Exam> Exams = csvread.GetRecords<Exam>().ToList();
        foreach (var rec in Exams) 
        {
          // Each record will be fetched and printed on the screen
          // checking values of rec here...
        }
    }
}

Вот как это анализируется: Значения

Почему он неправильно анализирует поля DateTime и почему эти свойства не заполняются?

QuestionNo
QuestionType
QuestionTitle
Questiontext
AnswerMatch
AttemptNo
OutOf


person MB34    schedule 06.04.2016    source источник
comment
ваш формат даты и времени - MM/dd/yyyy HH:nn. Должен быть MM/dd/yyyy HH:mm, с mm, а не nn.   -  person Arturo Menchaca    schedule 06.04.2016
comment
Это не изменило результат. Даже изменение его на M/d/yyyy HH:mm не сработало.   -  person MB34    schedule 06.04.2016


Ответы (2)


Вы можете добавить метод для синтаксического анализа DateTime следующим образом (вы даже можете установить точку останова и изменить параметры):

Map(t => t.AttemptStart).Name("Attempt Start")
    .ConvertUsing(new Func<CsvHelper.ICsvReaderRow, DateTime>(r =>
        {
            DateTime dateTimeValue;
            if (DateTime.TryParse(r["Attempt Start"], null,
                System.Globalization.DateTimeStyles.AllowWhiteSpaces, out dateTimeValue))
            {
                return dateTimeValue;
            }

            return default(DateTime);
        }));

Отсутствующие поля могут быть ошибками из-за проблемы синтаксического анализа DateTime. Это выглядит правильно на первый взгляд.

person Scoregraphic    schedule 06.04.2016
comment
Ваши переменные показывают, что вы должны использовать TryParseExact. - person MB34; 06.04.2016
comment
Пробовал это тоже с теми же нулевыми результатами: DateTime.TryParse(r["Attempt Start"], out dateTimeValue). На самом деле, добавление того, что ConvertUsing привело к тому, что значения были установлены на null, где до того, как они были установлены на 1/1/0001 12:00:00 AM, я изменил определение dateTimeValue на это, чтобы я не получал нули: DateTime dateTimeValue = DateTime.Now; и return dateTimeValue - person MB34; 06.04.2016
comment
Вы правы, я исправил свой код для успешного анализа DateTime на основе вашего примера (см. dotnetfiddle.net/Na4CPM ). Странно, что он даже не достигает вашей точки останова. Не могли бы вы .Ignore() указать некоторые свойства и проверить первое, вызывающее проблемы? - person Scoregraphic; 06.04.2016
comment
Тем не менее никогда не останавливается в точке останова. Я даже не уверен, что он использует карту, потому что я добавил .Ignore ко всем, кроме Attempt Start, и получил те же результаты со многими другими заполненными свойствами. Есть ли особый способ сказать ему использовать класс карты, я не вижу его в документах? - person MB34; 06.04.2016
comment
Я вижу это в документах, но не уверен, что это значит: вам нужно зарегистрировать карту классов в конфигурации. - person MB34; 06.04.2016
comment
Хорошо, я нашел это. ExamMap ExamsMap = new ExamMap(); csvread.Configuration.RegisterClassMap(ExamsMap); Это все исправило. Спасибо! - person MB34; 06.04.2016

Кажется, у меня все работает нормально. Все, что я сделал, было это.

csv.Configuration.RegisterClassMap<ExamMap>();
csv.GetRecords<Exam>().ToList().Dump();

Я получаю все поля, которые вам не хватает, тоже отлично отображаются.

CsvHelper будет анализировать все эти типы по умолчанию. Вы также можете добавить параметры преобразователя типов проще, если вам это нужно.

TypeConverterOptions

Вот полный пример из linqpad.

void Main()
{
    using (var stream = new MemoryStream())
    using (var reader = new StreamReader(stream))
    using (var writer = new StreamWriter(stream))
    using (var csv = new CsvReader(reader))
    {
        writer.WriteLine("Org Defined ID,Username,FirstName,LastName,Attempt #,Attempt Start,Attempt End,Section #,Q #,Q Type,Q Title,Q Text,Bonus?,Difficulty,Answer,Answer Match,Score,Out Of");
        writer.WriteLine(", testomalley, Test, O'Malley,1,2/3/2016 15:24,2/3/2016 15:28,,1,LA,Q(1) 1- 5 Part 1,\"Scenario 1 for Questions 1 through 5. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.  Part 1 - Sed ut perspiciatis unde omnis iste natus error sit voluptatem.\",FALSE,1,Here is my answer o grader. Isn't it brilliant ?,, 0, 2");
        writer.Flush();
        stream.Position = 0;

        csv.Configuration.RegisterClassMap<ExamMap>();
        csv.GetRecords<Exam>().ToList().Dump();
    }
}

public class Exam
{
    public int? OD_ID { get; set; }
    public string Username { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int AttemptNo { get; set; }
    public DateTime AttemptStart { get; set; }
    public DateTime AttemptEnd { get; set; }
    public int? SectionNo { get; set; }
    public int QuestionNo { get; set; }
    public string QuestionType { get; set; }
    public string QuestionTitle { get; set; }
    public string Questiontext { get; set; }
    public string Bonus { get; set; }
    public int Difficulty { get; set; }
    public string Answer { get; set; }
    public string AnswerMatch { get; set; }
    public int Score { get; set; }
    public int OutOf { get; set; }
}

public sealed class ExamMap : CsvHelper.Configuration.CsvClassMap<Exam>
{
    public ExamMap()
    {
        Map(m => m.OD_ID).Name("Org Defined ID");
        Map(m => m.Username).Name("Username");
        Map(m => m.FirstName).Name("FirstName");
        Map(m => m.LastName).Name("LastName");
        Map(m => m.AttemptNo).Name("Attempt #");
        Map(m => m.AttemptStart).Name("Attempt Start");
        Map(m => m.AttemptEnd).Name("Attempt End");
        Map(m => m.SectionNo).Name("Section #");
        Map(m => m.QuestionNo).Name("Q #");
        Map(m => m.QuestionType).Name("Q Type");
        Map(m => m.QuestionTitle).Name("Q Title");
        Map(m => m.Questiontext).Name("Q Text");
        Map(m => m.Bonus).Name("Bonus?").TypeConverterOption(true, "TRUE").TypeConverterOption(false, "FALSE");
        Map(m => m.Difficulty).Name("Difficulty");
        Map(m => m.Answer).Name("Answer");
        Map(m => m.AnswerMatch).Name("Answer Match");
        Map(m => m.Score).Name("Score");
        Map(m => m.OutOf).Name("Out Of");
    }
}
person Josh Close    schedule 07.04.2016
comment
Джош, видимо, ты не читал мой последний комментарий к посту @Scoregraphic. Я исправил проблему и разместил запрос на обновление вашей документации как проблему на GitHub. - person MB34; 08.04.2016