JoshClose / CsvHelper

Library to help reading and writing CSV files
http://joshclose.github.io/CsvHelper/
Other
4.75k stars 1.06k forks source link

Headers cannot start with a capital letter. #1078

Closed ksmiller99 closed 6 years ago

ksmiller99 commented 6 years ago

After using GetRecords, I get an exception that the header name was not found if the first letter of the property name is upper case. Here is the code I'm using:

using CsvHelper;
using System;
using System.Collections.Generic;
using System.IO;

namespace CsvTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var SettingsFileName = @"c:\temp\test.csv";
            var list = new List<Person>();
            list.Add(new Person("Kevin", DateTime.Now));
            list.Add(new Person("Angela", DateTime.Now));
            using (TextWriter writer = new StreamWriter(SettingsFileName))
            {
                var csv = new CsvWriter(writer);
                csv.WriteRecords(list); // where values implements IEnumerable
            }

            list.Clear();

            using (TextReader fileReader = File.OpenText(SettingsFileName))
            {
                var csv = new CsvReader(fileReader);
                csv.Configuration.HasHeaderRecord = true;
                var records = csv.GetRecords<Person>();

                try
                {
                    foreach (var record in records) //exception thrown here
                    {
                        list.Add(record); 
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);                   
                }

                Console.ReadKey();
            }
        }
    }

    class Person
    {
        public Person(string firstName, DateTime dateOfBirth)
        {
            FistName = firstName;
            DateOfBirth = dateOfBirth;
        }

        public string FistName { get; set; }
        public DateTime DateOfBirth { get; set; }

    }
}

The exact exception message is: Header matching ['firstName'] names at index 0 was not found. If you are expecting some headers to be missing and want to ignore this validation, set the configuration HeaderValidated to null. You can also change the functionality to do something else, like logging the issue.

If I change the property names to "firstName" and "dateOfBirth", it works. Am I doing someting wrong, or is there a work-around short of renaming the properties? I want to use this on existing classes and refactoring them to change the property names is not an option.

georgevanburgh commented 6 years ago

Person is not default constructable, and the only constructor for Person takes two named arguments - firstName and dateOfBirth. As a result - CsvHelper is attempting to use constructor mapping - but the capitalisation of headers in the CSV doesn't match that of the constructor. If you capitalise the constructor parameters correctly, this should work:

class Person
{
    public Person(string FirstName, DateTime DateOfBirth)
...

Similarly, if you add a default constructor to Person, CsvHelper can use that - and then set the properties correctly.

class Person
{
    public Person() { }
    public Person(string firstName, DateTime dateOfBirth) {
...

If you're unable the change the POCO in any way - you should be able to use PrepareHeadersForMatch to transform the headers into the correct format for your constructor. Example here.

using (TextReader fileReader = File.OpenText(SettingsFileName))
{
    var csv = new CsvReader(fileReader);
    csv.Configuration.PrepareHeaderForMatch = header =>
        CultureInfo.CurrentCulture.TextInfo.ToTitleCase(header);
    var records = csv.GetRecords<Person>();
...

Hope this helps 😄

ksmiller99 commented 6 years ago

That was exactly right - Thanks for clearing that up!