FileHelper reading in CSV where initial row specifies column names

405 Views Asked by At

For context I'm trying to parse in a 7dtd localization file. The contents of the file looks something akin to this:

Key,File,Type,UsedInMainMenu,NoTranslate,english,Context / Alternate Text,german,latam,french,italian,japanese,koreana,polish,brazilian,russian,turkish,schinese,tchinese,spanish
meleeToolCrowbar,items,Tool,,,Crowbar,,,,,,,,,,,,,,

The problem is this, the order of the data presented varies from file to file, where the 1st row specifies the item placement for each entry further in the file. I need to read in this file and merge it with multiple other files of similar construction.

I initially tried to just read in and use split string on comma to parse, but alas some of the fields may have commas in them (bounded by quotes). So when I started investigating potential solutions FileHelpers came up. However, from what I can tell this uses a static column to property definition, and that won't work for my context as a) order of the columns vary and b) not all columns are present.

Any help finding a solution would be much appreciated.

1

There are 1 best solutions below

0
netniV On

If you review the dynamic ClassBuilder part of the library, you can create a class that maps between the columns and the class you want by adding fields. Then use the CreateRecordClass object to create the actual class type to be used by the runtime engine.

You would need to parse the first row and then work out what columns you have. I do believe that you can use the delimited engine to do this work for you if you don't specify any columns or types.

It's more complex with FileHelpers than with say CSVHelper where you create the mapping in a simple way. If you use the CsvDataReader then you use ClassMap's to go between your column headers and the properties, even specifying specific indexes for the columns you actually want.

void Main()
{
    var config = new CsvConfiguration(CultureInfo.InvariantCulture)
    {
        HasHeaderRecord = false,
    };
    using (var reader = new StreamReader("path\\to\\file.csv"))
    using (var csv = new CsvReader(reader, config))
    {
        csv.Context.RegisterClassMap<FooMap>();
        csv.Context.RegisterClassMap<BarMap>();
        var fooRecords = new List<Foo>();
        var barRecords = new List<Bar>();
        while (csv.Read())
        {
            switch (csv.GetField(0))
            {
                case "A":
                    fooRecords.Add(csv.GetRecord<Foo>());
                    break;
                case "B":
                    barRecords.Add(csv.GetRecord<Bar>());
                    break;
                default:
                    throw new InvalidOperationException("Unknown record type.");
            }
        }
    }
}

public class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Bar
{
    public Guid Id { get; set; }
    public string Name { get; set; }
}

public sealed class FooMap : ClassMap<Foo>
{
    public FooMap()
    {
        Map(m => m.Id).Index(1);
        Map(m => m.Name).Index(2);
    }
}

public sealed class BarMap : ClassMap<Bar>
{
    public BarMap()
    {
        Map(m => m.Id).Index(1);
        Map(m => m.Name).Index(2);
    }
}