Home > front end >  CsvHelper exception Unhandled exception. CsvHelper.HeaderValidationException: Header with name when
CsvHelper exception Unhandled exception. CsvHelper.HeaderValidationException: Header with name when

Time:10-12

OK I have the following record definition:

    namespace Test09_CSVHelperTest
{
    public sealed record Filter
    {
        public Filter(string body, string category)
        {
            if (string.IsNullOrEmpty(body)) throw new ArgumentException(@"Value cannot be null or empty.", nameof(body));
            if (string.IsNullOrWhiteSpace(body))
                throw new ArgumentException(@"Value cannot be null or whitespace.", nameof(body));
            if (string.IsNullOrEmpty(category))
                category = "Unknown";
            var replaceRegex = new Regex(@"\s ",
                RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace |
                RegexOptions.CultureInvariant);
            Body = replaceRegex.Replace(body, @"\s*");
            Category = category;
        }

        #region Overrides of Object

        public override int GetHashCode()
        {
            return Body.GetHashCode();
        }

        public bool Equals(Filter? other)
        {
            return other is not null && other.Body.Equals(Body, StringComparison.InvariantCultureIgnoreCase);
        }

        #endregion

        //[Name("Body")]
        [Index(0)]
        public string Body { get; }
        [Ignore]
        //[JsonIgnore]
        public string Text => Body.Replace(@"\s*", " ");
        //[Name("Category")]
        [Index(1)]
        public string Category { get; set; }

    }

and I have the following run code:

namespace Test09_CSVHelperTest
{
    internal class Program
    {
        static void Main(string[] args)
        {
            var dirPath = @"d:\Temp01";
            var fileName1 = @"FiltersOutListExport.csv";
            // ReSharper disable once InconsistentNaming
            BindingList<Filter> FiltersOutList = new BindingList<Filter>();
            Directory.CreateDirectory(dirPath);

            for (var i = 0; i < 10; i  )
            {
                var category = $"Category #{i:D2}";
                for (var j = 0; j < 10; j  ) FiltersOutList.Add(new Filter($"Filter #{j:D2}", category));
            }

            using (var msTxWriter = new StreamWriter(Path.Combine(dirPath, fileName1) /*ms*/))
            using (var csvWriter = new CsvWriter(msTxWriter, CultureInfo.InvariantCulture))
                csvWriter.WriteRecords(FiltersOutList);

            
            BindingList<Filter> filters;
            
            using (var msReader = new StreamReader(Path.Combine(dirPath, fileName1)))
            using (var csvRead = new CsvReader(msReader,  CultureInfo.InvariantCulture))
            {
              >>  var temp01 = csvRead.GetRecords<Filter>().ToArray();
                filters = new BindingList<Filter>(temp01);
            }

            Console.WriteLine(filters);
            foreach (var filter in filters) Console.WriteLine(filter.Text, "\t", filter.Category);
        }
}

at the line marked with ">>" I get the following Exception:

Unhandled exception. CsvHelper.HeaderValidationException: Header with name 'body'[0] was not found.
Header with name 'category'[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.

IReader state:
   ColumnCount: 0
   CurrentIndex: -1
   HeaderRecord:
["Body","Category"]
IParser state:
   ByteCount: 0
   CharCount: 15
   Row: 1
   RawRow: 1
   Count: 2
   RawRecord:
Body,Category


   at CsvHelper.Configuration.ConfigurationFunctions.HeaderValidated(HeaderValidatedArgs args)
   at CsvHelper.CsvReader.ValidateHeader(Type type)
   at CsvHelper.CsvReader.ValidateHeader[T]()
   at CsvHelper.CsvReader.GetRecords[T]() MoveNext()
   at System.Collections.Generic.LargeArrayBuilder`1.AddRange(IEnumerable`1 items)
   at System.Collections.Generic.EnumerableHelpers.ToArray[T](IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at Test09_CSVHelperTest.Program.Main(String[] args) in G:\My Repos\EGYPHARAOH\2022Projects-Trials\Test09-CSVHelperTest\Program.cs:line 46

Question is: Am I doing something wrong or this is a bug? if it is me then how to do it? if it is a bug is there a work-around?

Things I wish my helpers to take into consideration:

  1. I have googled and did not reach to an answer
  2. I have searched the very stackoverflow.com and none of the available might looks similar gives an answer.
  3. I have used the [Index(0)] and the [Name("PropertyName")] tricks and they did not work, you can find them commented in the record definition code.
  4. The file was written by CsvHelper.CsvWriter itself, no modification or alternation made to the file.

Please, try to help on the noob level kind of answer :) sample of the output file content as is:

Body,Category
Filter\s*#00,Category #00
Filter\s*#01,Category #00
Filter\s*#02,Category #00
Filter\s*#03,Category #00
Filter\s*#04,Category #00
Filter\s*#05,Category #00
Filter\s*#06,Category #00
Filter\s*#07,Category #00
Filter\s*#08,Category #00
Filter\s*#09,Category #00
Filter\s*#00,Category #01
Filter\s*#01,Category #01
Filter\s*#02,Category #01
Filter\s*#03,Category #01
Filter\s*#04,Category #01
Filter\s*#05,Category #01
Filter\s*#06,Category #01
Filter\s*#07,Category #01
Filter\s*#08,Category #01
Filter\s*#09,Category #01
Filter\s*#00,Category #02
Filter\s*#01,Category #02
Filter\s*#02,Category #02
Filter\s*#03,Category #02
Filter\s*#04,Category #02
Filter\s*#05,Category #02
Filter\s*#06,Category #02

CodePudding user response:

The cause of the problem is CsvHelper trying to match the csv record headers (Body, Category) to the Filter constructor parameters in a case-sensitive manner, and there are no Body and Category constructor parameters.

The solution is relatively simple. You can either change the constructor parameter names from

public Filter(string body, string category)

to

public Filter(string Body, string Category)

which is unfortunately not adhering to the common C# style of using camelcase/lowercase for method/constructor parameter names.

Or you can use CsvHelper's [Name] attribute to define the csv field name for each constructor parameter and keep the camelcase/lowercase constructor parameter names like:

public Filter(
    [Name("Body")] string body,
    [Name("Category")] string category
)
  • Related