using csvhelper (nuGET) with C# MVC to import CSV files
Asked Answered
S

3

7

https://joshclose.github.io/CsvHelper/ available via NuGet is used to read and write CSV files.

CsvHelper allows you to read your CSV file directly into your custom class.

As following was shown in a previous question

var streamReader = // Create a reader to your CSV file.
var csvReader = new CsvReader( streamReader );
List<MyCustomType> myData = csvReader.GetRecords<MyCustomType>();

CsvReader will automatically figure out how to match the property names based on the header row (this is configurable). It uses compiled expression trees instead of reflection, so it's very fast.

It is also very extensible and configurable.

I'm basically trying to work out how to read in a CSV file with headers (unknown names) and read the records into a custom object.

There is no documentation on this at all so wondered if anyone knew how to use CsvReader to put the values in order into an array of strings or how would you recommend dealing with this?

Screeching answered 31/3, 2011 at 8:3 Comment(1)
The website seems to have moved to joshclose.github.io/CsvHelperTizes
S
9

This is my first version, I will update as I amend things and make it more complete but this gives me all the data in string arrays.

   [HttpPost]
        public ActionResult UploadFile(HttpPostedFileBase file)
        {

            ICsvParser csvParser = new CsvParser(new StreamReader(file.InputStream));
            CsvReader csvReader = new CsvReader(csvParser);
            string[] headers = {};
            List<string[]> rows = new List<string[]>();
            string[] row;
            while (csvReader.Read())
            {
                // Gets Headers if they exist
                if (csvReader.HasHeaderRecord && !headers.Any())
                {
                    headers = csvReader.FieldHeaders;
                }
                row = new string[headers.Count()];
                for (int j = 0; j < headers.Count(); j++)
                {
                    row[j] = csvReader.GetField(j);
                }
                rows.Add(row);
            }
            ImportViewModel model = new ImportViewModel(rows);
            return View(model);
        }
Screeching answered 5/5, 2011 at 22:20 Comment(0)
S
5

There is a CsvFieldAttribute that you can put on your property where you can either put the name of csv field, or the index of the csv field. Name will only work if there is a header row in the csv file.

public class MyCustomClass
{
    [CsvField( FieldIndex = 1 )]
    public string Property1 { get; set; }

    [CsvField( FieldIndex = 0 )]
    public string Property2 { get; set; }

    [CsvField( FieldIndex = 2 )]
    public string Property3 { get; set; }
}

If all you want to do is read a record into a string array in the order that it's in in the file, you can just use CsvParser instead of CsvReader. Calling CsvParser.Read() returns a string[]. CsvReader uses CsvParser to read the raw data.

https://github.com/JoshClose/CsvHelper/wiki/Fluent-Class-Mapping

Surgeon answered 2/4, 2011 at 5:6 Comment(9)
So should I use csvreader to grab the header and then csvparser in a while loop to grab the row data? Could you provide an example for this?Screeching
See my answer below. Is this the best was of grabbing all the data? The next stage would be to allow the user to match there columns to properties on my entities. I'm a bit worried about storing the whole CSV between the requests though. Thinking store in Session or DB but both will be fairly heavy (potentially)Screeching
For more details, you could also refer to this wiki page written by Josh Close(surprise!) github.com/JoshClose/CsvHelper/wiki/Fluent-Class-MappingSearchlight
@JoshClose Hey Josh, just wanted to mention that this answer was really helpful, and thanks for the great project. Please consider documenting CsvParser in the wiki - I'm doing something kind of strange with a jagged csv format, and I almost ended up using a crappy old unsupported csv project because I didn't think yours handled non-mapped scenarios until I happened upon this answer. :)Seat
@BrianMacKay The reader also has various other Get methods if you want to do it manually. i.e. T Get<T>( int columnIndex )Surgeon
@JoshClose Yes, I saw that you are planning to cut the mapping attributes in v2, right?Seat
@BrianMacKay Yes. You can't do everything in an attribute. Reference mapping is a good example. It's an absolute mess to have that work through attributes. The mapping classes are just a lot more flexible and easy to work with. The files will still auto map if you don't specify a mapping, like they did with the attributes. If you have an concerns or features, feel free post on the google group.Surgeon
@JoshClose Thanks Josh... FWIW, in my situation, there are no headers so I had to do index mapping, and I actually found attributes to be simple and clean. I tried the fluent method first and it didn't work instantly. It sounds like attributes must get nightmarish in more complex situations though. Thanks again. :)Seat
@BrianMacKay Yes, it gets real bad. Class mapping will only map the properties you specifically map, and the other are ignored. There is a little documentation on this (I need to add lots more). github.com/JoshClose/CsvHelper/wiki/Fluent-Class-MappingSurgeon
H
1

I know this is not related to CVSHelpers but you may like to consider the FileHelpers project

It allows you to decorate fields on an object with attributes so that it represents a row in the csv file and then use a FileHelperEngine to read the file in - resulting in an array of objects each one representing a row

see this quick start on reading delimited files

Hustler answered 31/3, 2011 at 8:12 Comment(2)
What if you don't know the header names and don't know the quantity does it handle that? Can you get it to parse to an array of strings or something similar? How does this cope with special characters and edge cases? Does it cope quite well?Screeching
just found this: #2356919Screeching

© 2022 - 2024 — McMap. All rights reserved.