Extension method for converting generic lists to CSV in C# [Updated]


Over a year ago I published a post which showed a c# extension method which can be use to convert a generic  list to CSV. Recently, I came across a problem in the code. It does not handle Nullable<T> properly. The following is an update to the code which fixes the Nullable<T> issues. I also added a cheap option which converts camel case headers to distinct words.

public static string ToCSV<T>(this IEnumerable<T> list, bool showheader = true, bool processHeaders = false)
 {
 var type = typeof(T);
 var properties = type.GetProperties();

//Setup expression constants
 var param = Expression.Parameter(type, "val");
 var doublequote = Expression.Constant("\"");
 var doublequoteescape = Expression.Constant("\"\"");
 var comma = Expression.Constant(",");

//Convert all properties to strings, escape and enclose in double quotes
 var propq = (from prop in properties
 let tostringcall = Expression.Call(typeof(Convert).GetMethod("ToString", new Type[] { typeof(object) }), Expression.Convert( Expression.Property(param, prop), typeof(object)))
 let replacecall = Expression.Call(tostringcall, typeof(string).GetMethod("Replace", new Type[] { typeof(String), typeof(String) }), doublequote, doublequoteescape)
 select Expression.Call(typeof(string).GetMethod("Concat", new Type[] { typeof(String), typeof(String), typeof(String) }), doublequote, replacecall, doublequote)
 ).ToArray();

//Convert an instance of the object to a single csv line
 var concatLine = propq[0];
 for (int i = 1; i < propq.Length; i++)
 concatLine = Expression.Call(typeof(string).GetMethod("Concat", new Type[] { typeof(String), typeof(String), typeof(String) }), concatLine, comma, propq[i]);

var method = Expression.Lambda<Func<T, String>>(concatLine, param).Compile();

if (showheader)
 {
 //Create header row
 var header = String.Join(",", properties.Select(p => processHeaders ? Regex.Replace(p.Name, "(\\B[A-Z])", " $1").Trim() : p.Name).ToArray());

return header + Environment.NewLine + String.Join(Environment.NewLine, list.Select(method).ToArray());
 }
 else
 {
 return String.Join(Environment.NewLine, list.Select(method).ToArray());
 }
 }
Advertisements

Extension method for converting generic lists to CSV in C#


I created the following class so I could easily convert a generic list to a CSV string.  This may be handy when you want to quickly export a moderately sized result set to Microsoft Excel.

    public static class CsvConverter
    {
        public static string ToCSV<T>(this IEnumerable<T> list)
        {
            var type = typeof(T);
            var props = type.GetProperties();

            //Setup expression constants
            var param = Expression.Parameter(type, "x");
            var doublequote = Expression.Constant("\"");
            var doublequoteescape = Expression.Constant("\"\"");
            var comma = Expression.Constant(",");

            //Convert all properties to strings, escape and enclose in double quotes
            var propq = (from prop in props
                         let tostringcall = Expression.Call(Expression.Property(param, prop), prop.ReflectedType.GetMethod("ToString",new Type[0]))
                         let replacecall = Expression.Call(tostringcall, typeof(string).GetMethod("Replace", new Type[] { typeof(String), typeof(String) }), doublequote, doublequoteescape)
                         select Expression.Call(typeof(string).GetMethod("Concat", new Type[] { typeof(String), typeof(String), typeof(String) }), doublequote, replacecall, doublequote)
                         ).ToArray();

            var concatLine = propq[0];
            for (int i = 1; i < propq.Length; i++)
                concatLine = Expression.Call(typeof(string).GetMethod("Concat", new Type[] { typeof(String), typeof(String), typeof(String) }), concatLine, comma, propq[i]);

            var method = Expression.Lambda<Func<T, String>>(concatLine, param).Compile();

            var header = String.Join(",", props.Select(p => p.Name).ToArray());

            return header + Environment.NewLine + String.Join(Environment.NewLine, list.Select(method).ToArray());
        }
    }

Adding this as an extension method for IEnumerable may not be the best thing since the above will fail if T is an object with no properties. However, you probably wouldn’t be converting something like a List of string to CSV anyways. If you are then you can simply use the following code

var csv = string.join(",",list.ToArray());  

The following console application sample demonstrates how the new method can be used. I hope someone finds this useful.

   class Program
    {
        static void Main(string[] args)
        {
            var list = new List<person>();
            var limit = 100;

            for (int x = 0; x < limit; x++)
            {
                var fname = "Ron";
                var lname = "Obvious";

                list.Add(new person()
                {
                    Age = x,
                    FirstName = fname,
                    LastName = lname
                });

            }
         
            var csv = list.ToCSV();

            Console.Write(csv);
            Console.ReadLine();

        }
    }

    class person
    {
        public int Age { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }