CSVHelper — сопоставление CSV с переменным заголовком объекту

#c# #csv #mapping #csvhelper

#c# #csv #сопоставление #csvhelper

Вопрос:

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

 public class Foo
{
    public string A { get; set; }
    public int? B { get; set; }
    public double? C { get; set; }
}
  

Файл 1:

 A   B   C
string  1   1.0
  

Файл 2:

 Bee Sea
1   1.0
  

Когда формат заголовка постоянен, это легко сделать с помощью CSVHelper, используя CsvClassMap:

 public sealed class FooMap : CsvClassMap<Foo>
{
    public FooMap()
    {
        Map(x => x.A).Name("A");
        Map(x => x.B).Name("B");
        Map(x => x.C).Name("C");
    }
}
  

Однако это становится сложным, когда формат заголовка является переменным.

  • Файлы могут различаться по ширине и высоте.
  • Порядок заголовков не фиксирован.
  • Свойство определяется максимум один раз для каждого файла.
  • Каждое свойство может относиться к различным именам заголовков.
  • Имена заголовков являются эксклюзивными для одного свойства.
  • Не все свойства обязательно определены в файле.

Каков был бы наилучший способ сопоставления с этим объектом?

Я полагаю, что я буду заполнять таблицу сопоставления заголовков со свойствами уникальным ключом в столбце заголовки, а затем искать соответствующее свойство каждого заголовка.

Текущее направление исследования:

 public sealed class FooMap : CsvClassMap<Foo>
{
    public FooMap()
    {
    }

    public void SetHeaders(List<string> headers)
    {
        var dictionary = new Dictionary<string, List<string>>();
        dictionary.Add("A", new List<string>() { "A", "Aay" });
        dictionary.Add("B", new List<string>() { "B", "Bee" });
        dictionary.Add("C", new List<string>() { "C", "Sea" });

        ...

    }
}
  

Комментарии:

1. Имеют ли эти файлы какой-либо схожий формат? Например: все файлы содержат как минимум поля A и B.

2. @raidensan Скорее всего, но не обязательно. Файлы будут одним и тем же объектом, только с нулевыми значениями, где нет совпадающих столбцов.

Ответ №1:

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

Вот пример использования LINQPad:

 void Main()
{
    using (var stream = new MemoryStream())
    using (var writer = new StreamWriter(stream))
    using (var reader = new StreamReader(stream))
    using (var csv = new CsvReader(reader))
    {
        writer.WriteLine("A Bee C");
        writer.WriteLine("string    1   1.0");
        writer.Flush();
        stream.Position = 0;

        csv.Configuration.Delimiter = " ";
        csv.Configuration.WillThrowOnMissingField = false;
        csv.Configuration.RegisterClassMap<FooMap>();
        csv.GetRecords<Foo>().ToList().Dump();
    }
}

public class Foo
{
    public string A { get; set; }
    public int? B { get; set; }
    public double? C { get; set; }
    public Guid? D { get; set; }
}

public class FooMap : CsvClassMap<Foo>
{
    public FooMap()
    {
        Map( m => m.A ).Name( "A", "Aay" );
        Map( m => m.B ).Name( "B", "Bee" );
        Map( m => m.C ).Name( "C", "Sea" );
        Map( m => m.D ).Name( "D", "Dee" );
    }
}
  

Результат:

 A: string
B: 1
C: 1
D: null