Home > .net 2.0, .net 3.0, .net 3.5, c#, General Development, Microsoft, NHibernate, Visual Studio 2008 > Part 2: Converting Generic Collections to CSVs

Part 2: Converting Generic Collections to CSVs

February 6, 2009

Part 1 | Part 2

In my last post, I wrote up the tests needed to meet our customer’s requirements, now I’m ready to flush out the tests.

Objective #1 – Export Collection to CSV With All Properties

Each of my tests implement one of two extension methods, ToDelimitedFile and ToDelimitedRow.  Collections focus on File whereas objects (singles) use the Row. 

Unfortunately, I couldn’t find a 100% accurate way to use the generic constraints—too bad we can’t put in “where T : !ICollection<T>”  Hence the separate methods.

For me, extension methods sit outside of my actual classes in their own happy static {whatever}Extensions class.  The CSV Conversions are no different.

For Test #1 to pass, we need to implement the extension method.

public static string ToDelimitedFile<T>(this ICollection<T> convertible)

{

       return DelimitedExporter.SerializeCollection(convertible);

}

This paves the way for creating our next class and method, DelimitedExporter and SerializeCollection.  The DelimitedExporter class will contain all of the methods and logic for converting our collections into CSV “lines”.  Let’s implement SerializeCollection real quick with some dummy data and rerun the test.

public static string SerializeCollection<T>(ICollection<T> convertible)

{

       return “Name,Level”;

}

Pass!  Test passes!

Now, of course, since the test passes, we’re ready to flush out the details of the SerializeCollection method.

Creating the Header Rows

Since we know we’ll be working with strings and doing a large number of appends, let’s start out by creating a StringBuilder to hold all of that text information.

public static string SerializeCollection<T>(IEnumerable<T> convertible)

{

       var stringBuilder = new StringBuilder();

 

       //grab properties of the method to create columns

       var columns = typeof(T).GetProperties();

 

GetProperties is a method on Type that returns all public properties as PropertyInfo objects.  These properties will be used to create the column header names for our CSV.

       // build header row

       stringBuilder.Append(BuildHeaderRowFrom(columns));

 

So let’s “build” our header row.  I’ve extracted this into a separate method from the start—you could have one mega method and refactor it down later.

 

private static string BuildHeaderRowFrom(IEnumerable<PropertyInfo> columns)

{

       var headerString = string.Empty;

       var counter = 0;

       foreach (PropertyInfo column in columns)

       {

              counter++;

             headerString += column.Name;

             if (counter != columns.Count)

                     headerString += “,”;

       }

       headerString += Environment.NewLine;

 

       return headerString;

}

The BuildHeaderRowFrom method takes the PropertyInfo enumeration from our GetProperties() call and iterates through it, capturing the Name of each property and appending it to a string.

Let’s close up the SerializeCollection() method …

       return stringBuilder.ToString();

}

… and run the test again and everything should pass up to this point.

Now we can focus on the data rows.

Creating the Data Rows

Our next task in SerializeCollection is to flush out how each enumerable object will be converted into a row in our CSV.

After our BuildHeaderRowsFrom() method, let’s now append the data rows with:

       // build item rows

       stringBuilder.Append(BuildDataRowsFrom(collection, columns));

BuildDataRowsFrom will take the original collection of objects (an IEnumerable<T>) and the columns we parsed for the header as parameters.

private static string BuildDataRowsFrom<T>(

       IEnumerable<T> collection,

       IEnumerable<PropertyInfo> columns)

{

       var rowString = string.Empty;

       foreach (T item in collection)

       {

              int counter = 0;

              var columnCount = columns.Count();

             foreach (PropertyInfo column in columns)

             {

                     counter++;

                     var value =

                           column.GetValue(item, null) ?? string.Empty;

 

                     // Append column value to the row.

                     rowString += “{0}”.AsFormatFor(value);

 

                     // Append delimiter if _not_ the last column.

                     if (counter != columnCount)

                           rowString += “,”;

              }

              rowString += Environment.NewLine;

       }

       return rowString;

}

Note: I could use a StringBuilder inside of this; however, the StringBuilder seemed to take a bit longer with the tests of 50,000ish records (max in the system I was designing this for).  A later revision may be to further examine ways to improve performance.

 

So, what’s all this do?

 

For the most part, it reads as a sentence:  For each item in the collection, loop through each known column and fetch the value from that item.  If the item is null, simply return an empty string. 

 

Since I’m using IEnumerable<T> rather than ICollection<T>, I don’t have access to a Count property (without calling the Linq extension method, so I’ve moved that outside of the foreach loop.  Perhaps there’s a better generic collection to be using, but that’s for later revisions.

 

Again, run our test again, and it should pass AND we should now have data.

 

can_convert_list_of_objects_to_csv : Passed

*** ConsoleOutput ***
Name,Level
SchoolA,Elementary
SchoolB,Elementary
SchoolC,Elementary
SchoolD,Elementary

 

Excellent.

 

That’s it for this post.  In the next part, I’ll start in on the second objective—being able to specify which parameters are picked up by the DelimitedExporter.

 

 

  1. March 1, 2009 at 2:28 am

    Just passing by.Btw, you website have great content!

    _________________________________
    Making Money $150 An Hour

  1. No trackbacks yet.
Comments are closed.
%d bloggers like this: