2

I am making a site that consumes a csv file this file can come in 2 formats(maybe more in the future).

Structure 1

Header 1 Header 2 Header 3 Header 4 
a          b      c       d
x          x      x       x

Structure 2

Header 1 Header 4
a          d
x          x

the above is how it would be shown in excel(if looking at raw it would be all comma separated)

The reason why I want to have 2 structures is because I am using trying to leverage a 3rd party site that user can export their data from. This site exports it as a csv file with the first row being the headers. I really only care about 2 of the headers and the reset is not needed at this current time (but you have to export all columns can't pick and choose).

The second structure is if the user does not wish to use this site because they don't want to, uncomfortable to do so and etc. They have an option of opening excel up and writing the data manually in and then saving it as a csv file.

So for manual people I want to make it as simple as possible as if I am not using Header 2 and Header 4 data why should I bother getting them to enter it in? However at the same time if people go through the first way and export the data I don't want them to have to go and load the file into excel up and remove 2 columns.

I am going to require the header must always be intact and be the first row. The only idea I came up with is read the first row and see the order of the headers. If it has 4 headers in the exact order then render it one way. If only 2 headers in that order render it another way.

I know that FileHelpers has the ability to do multiple delimiters and choose how to render it but since I am looking at headers I am not sure if this baked in or if I need to somehow write it myself and then tell it what to do.

Does anyone have an idea if I can do this with filehelpers?

Edit this is what I have so far

MultiRecordEngine engine = new MultiRecordEngine(typeof(Format2), typeof(Format1));
    engine.RecordSelector = new RecordTypeSelector(CustomSelector);

    using (TextReader textReader = new StreamReader(stream))
    {
        if (engine.RecordType == typeof(Format2))
        {
            var myArry = engine.ReadStream(textReader) as Format2[];

        }
        else if(engine.RecordType == typeof(Format1))
        {
            var myArry = engine.ReadStream(textReader) as Format1[];
        }



    }
Cœur
  • 37,241
  • 25
  • 195
  • 267
chobo2
  • 83,322
  • 195
  • 530
  • 832

2 Answers2

5

Here are a couple of suggested approaches:

Read the first line of the file (outside of FileHelpers) and determine which format is being used before creating the engine

if (firstLineOfFile.Contains("Header 2"))
    FileHelperEngine engine = new FileHelperEngine(typeof(Format1)); 
else
    FileHelperEngine engine = new FileHelperEngine(typeof(Format2)); 

Alternatively, you can use the MultiRecordEngine

MultiRecordEngine engine;  
engine = new MultiRecordEngine(typeof(Format1), typeof(Format2)); 
engine.RecordSelector = new RecordTypeSelector(CustomSelector); 

with a selector method something like

Type CustomSelector(MultiRecordEngine engine, string record) 
{ 
    // count the separators to determine which format to return
    int separatorCount = record.Count(f => f == ',');
    if (separatorCount == 4) 
        return typeof(Format1); 
    else 
        return typeof(Format2); 
} 

(The MultiRecordEngine can handle more formats in the future - it has a constructor with a params parameter)

shamp00
  • 11,106
  • 4
  • 38
  • 81
  • Ya the first way was the way I was thinking I would end up of doing it but I like your second way. How does record get filled? and what is the engine param used for? – chobo2 Jan 10 '12 at 17:03
  • The FileHelper engine fills them for you. The `CustomSelector` function is a delegate that the FileHelpers engine calls for each record. It will populate `engine` with itself and `record` with the current record and it will then use the `Type` you return to determine which of `Format1` and `Format2` to use. – shamp00 Jan 10 '12 at 17:27
  • Ok. I guess once I start to implement it. It will make more sense. – chobo2 Jan 10 '12 at 18:40
  • So how do I later on figure out what object to use? Do I have to for every record a "as" and see it can cast to that object? – chobo2 Jan 11 '12 at 23:35
  • No, the `record` parameter in the `CustomSelector` delegate is a `string`. It represents a row in your import format. You have to supply some logic which determines which FileHelpers class to use based on the contents of the row (in the example I provided I suggested counting the commas, but you can use whatever suits). The `CustomSelector` delegate must return the `Type` of the FileHelpers class which should be used for the row. The [example](http://www.filehelpers.com/example_multirecords.html) here should help. – shamp00 Jan 12 '12 at 09:27
  • But does it always take the first record and pass that into the customSelector? – chobo2 Jan 12 '12 at 16:56
  • Also in the example they just use an object array they never actually convert it to a class. CustomSelector may return a type it goes returned into "RecordTypeSelector"? I see a engine.RecordType is that what I should be using? – chobo2 Jan 12 '12 at 17:09
1

With FileHelpers you will need to know the format ahead of time.

Which means that you will have to detect (using the method you described) which format the file is in prior to using FileHelpers to parse it.

NotMe
  • 87,343
  • 27
  • 171
  • 245