CsvHelper не может найти заголовок столбца, которого нет

У меня есть проект с использованием CsvHelper, который я написал на VS2017 еще в 2017 году и тогда он успешно работал. На этой неделе я пытаюсь перетащить код в VS2022, .Net Core и последнюю версию CsvHelper (33.0.1). При этом я обнаружил странную ошибку в CsvHelper.

В основном я использую функцию Получить записи классов, и у меня есть класс, который охватывает записи в моих данных CSV и соответствует регистру каждого заголовка и т. д. (и это работало в 2017 году). Например:

public class Foo
{
    public int MyInt { get; set; }
    public string MyString { get; set; }
    public ComputedClass CustomData {get; set; }
    ...
}

Где «CustomData» — это класс, который я вычисляю на основе некоторых полей в «Foo», поэтому он не включается в необработанные данные CSV.

У меня также есть класс сопоставления CsvHelper для Foo, где я говорю CsvHelper игнорировать поле «CustomData» (это новое в моем коде VS2022, и я сделал это, чтобы попытаться устранить проблему, с которой я столкнулся)

public sealed class FooMap : ClassMap<Foo>
{
    public FooMap()
    {
        AutoMap(CultureInfo.InvariantCulture);
        Map(m => m.CustomData).Ignore();
    }
}

Соответствующие данные CSV (но в необработанном файле CSV нет имени поля «CustomData», и это те же данные, которые я тестировал в 2017 году)

MyInt,MyString,...
1,"One",...
2,"Two",...

И я передаю эту конфигурацию в CsvHelper и вызываю программу чтения следующим образом (где «читатель» — это программа чтения текстовых файлов, которая открыла файл CSV.

CsvConfiguration config = new(CultureInfo.InvariantCulture)
{
    HasHeaderRecord = true,
    TrimOptions = TrimOptions.Trim,
    MissingFieldFound = null
};
using var csv = new CsvReader(reader, config);
csv.Context.RegisterClassMap<FooMap>();
while (csv.Read())
{
    var row = csv.GetRecord<Foo>();
    row.CustomData = new ComputedClass(row.MyInt,row.MyString);
    ...
}

И вот здесь начинается веселье. При выполнении этого кода GetRecord() генерирует исключение, связанное с заголовками, и сообщение об исключении начинается с:

Header with name 'myIntParam'[0] was not found.
Header with name 'myStringParam'[0] was not found.
...

Поля, о которых сообщается, что они отсутствуют, не имеют того же регистра, что поля, которые я определил в классе Foo, и не соответствуют тому, что находится в реальном файле CSV. Я выполнил полный поиск своего решения с учетом регистра, и у меня есть символы «myIntParam» и «myStringParam», но они используются только в качестве параметров конструктора «ComputedClass».

public ComputedClass(int myIntParam, string myStringParam)
{
   ...
}

Таким образом, кажется, что даже несмотря на то, что я говорю CsvHelper игнорировать свойство CustomData, CsvHelper все равно находит параметры конструктора для этого свойства. Но поскольку они являются параметрами конструктора, их нельзя добавить в класс FooMap и игнорировать.

Как устранить это исключение?

В строке не хватает закрывающей скобки Map(m => m.CustomData.Ignore();

Adam Silenko 18.07.2024 22:11

Согласно этому ответу CsvHelper 27.1.1 будет использовать конструктор без параметров, если он присутствует. Если нет, он будет использовать конструктор с наибольшим количеством параметров. Есть ли у вас конструктор без параметров, определенный для этого класса? Я думаю, вам это не понадобится, если вы проигнорируете это, так что это немного раздражает.

mason 18.07.2024 22:24

@mason Я не определил никакого явного конструктора для Foo, поэтому он использует конструктор без параметров по умолчанию, предоставляемый компилятором. Но у ComputedClass есть явный конструктор с параметрами. Теперь я думаю, что, возможно, обнаружил настоящую ошибку в CsvHelper из-за того, что отражение зашло слишком далеко при построении списка заголовков, ожидаемых в файле CSV.

Peter M 18.07.2024 22:54

@mason Еще мне, возможно, придется попробовать удалить конструктор из ComputedClass и сделать его без параметров. Тогда отражению будет нечего искать.

Peter M 18.07.2024 23:01

@mason Мой последний комментарий об удалении конструктора ComputedClass помог. Позже я напишу свой ответ. И сообщить об ошибке.

Peter M 18.07.2024 23:08
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
5
87
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Основываясь на том, что @Mason предложил этот ответ на другой вопрос CsvHelper, я начал думать о том, как отражение, используемое CsvHelper, анализирует Foo, чтобы создать список заголовков, которые должны были быть в данных CSV.

По какой-то причине кажется, что CsvHelper немного переусердствовал и предполагал, что параметры конструктора ComputedClass должны быть заголовками. Моей первой попыткой исправить это было удаление параметров из конструктора и добавление функции Set(), которая принимала те же параметры. Это сработало.

Но потом я понял, что в моем коде есть еще один класс, который не вызывает проблем, хотя у него тоже есть параметризованный конструктор. Глядя на этот класс, я увидел, что определил явный конструктор по умолчанию наряду с параметризованным.

Я применил эту идею к ComputedClass, в результате чего получился код, который не вызывает проблемы:

// This satisfies CsvHelper when constructing the list of headers
[Obsolete()]
public ComputedClass()
{
}

// This is what I use in code, and CsvHelper now ignores
public ComputedClass(int myIntParam, string myStringParam)
{
   ...
} 

Кроме того, я удалил ссылки FooMap и RegisterClassMap, и код продолжил работать.


Я просто искал способы пометить конструктор как «Не использовать», когда столкнулся с другим вопросом SO, в котором задавалась аналогичная вещь о (де)сериализаторах XML.

Уведомить разработчика о методе «НЕ ИСПОЛЬЗОВАТЬ»

Другие вопросы по теме

Почему исключение «Exception», похоже, не перехватывает исключение?
«Exception.Exception(SerializationInfo, StreamingContext)» устарело» – лучшая практика реализации исключений?
Неожиданное поведение генератора, если он не назначен переменной
Как использовать try-catch (IOException) для BufferedWriter.write, вложенного в if-else?
Где следует создавать исключения в шестиугольной архитектуре?
Как вернуть пользовательское сообщение об ошибке для неверного значения перечисления в Spring Boot RequestBody?
Как написать устойчивый объектно-ориентированный метод для таблицы обновления SQL с повтором в catch
Где из стандарта я прочитал, что исключения производного класса, хранящиеся по ссылке на базовый класс, отсекаются при обнаружении?
Как перенаправить исключение, перехваченное промежуточным программным обеспечением, обратно в метод действия контроллера в C# ASP.NET Core?
Ресурс не найден: как найти идентификатор строкового ресурса в приложении MAUI?