1

I'm trying to map some variable-format tab-separated files to my class, Foo:

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

File 1:

A   B   C
string  1   1.0

File 2:

Bee Sea
1   1.0

When the header format is constant, this is easy to do with CsvHelper using a 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");
    }
}

However, this becomes complex when the header format is variable.

  • The files can vary in width and height.
  • Header order is not fixed.
  • A property is only defined a maximum of once per file.
  • Each property could relate to a variety of header names.
  • Header names are exclusive to a single property.
  • Not all properties are necessarily defined in a file.

What would be the best way of mapping to this object?

I imagine I will be populating a mapping table for headers to properties with a unique key on the headers column, then looking up each header's respective property.

Current direction of investigation:

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" });

        ...

    }
}
2
  • Does these files have similar format in a way? For example: all files contain at least fields A and B. Commented Oct 19, 2016 at 13:31
  • @raidensan Most likely, but not necessarily. The files will be the same object though, just with nulls where there are no matching columns. Commented Oct 19, 2016 at 16:08

1 Answer 1

3

You can specify multiple names per property and make it not throw an exception if there is a missing field.

Here is an example using 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" );
    }
}

The result:

A: string
B: 1
C: 1
D: null
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.