16

How can I write all values (properties) into an csv formated string in C#? e.g.:

class Person(string firstName, string lastName, int_age);
Person person = new Person("Kevin","Kline",33);

now I want a string "Kevin;Kline;33"

In other words I want to serialize an object into CSV

StuartLC
  • 100,561
  • 17
  • 199
  • 269
uhu
  • 1,612
  • 4
  • 17
  • 26
  • Your question needs far more background and examples of data/code to avoid being a candidate for closure. – KP. Jun 15 '12 at 12:53
  • 1
    Can you show us what you already tried? – LolCat Jun 15 '12 at 12:53
  • @nonnb: you can post this as an answer, it's helpful. – Groo Jun 15 '12 at 12:56
  • 1
    Also, this is perhaps a minor nitpick, but the comma-separated value format uses commas to separate the values. You seem to be using semicolons. – Thom Smith Jun 15 '12 at 13:21
  • You are welcome. Also, since you are new to StackOverflow, I would like to inform you that you can upvote good answers and accept the answer that helped you the most by checking the tick mark next to the Answer. On this site an upvote or an accepted answer counts as a "thanks". – Olivier Jacot-Descombes Jan 16 '13 at 14:10

6 Answers6

14

Have a look at Josh Close's excellent CSVHelper library

var person = new Person("Kevin","Kline",33);
using (var csv = new CsvWriter(new StreamWriter("file.csv")))
{
    csv.Configuration.HasHeaderRecord = false;
    csv.Configuration.Delimiter = ';';
    csv.WriteRecord(person);
}

Output:

Kevin;Kline;33
Community
  • 1
  • 1
StuartLC
  • 100,561
  • 17
  • 199
  • 269
  • Is it possible to use CSVHelper, without the saving file.csv part? I don't want to write the file in the Hd, I intend to send the file as a response to the web user. – Lombas Nov 27 '15 at 17:39
  • 4
    Best part is that it is availiable for .Net Core – Dživo Jelić Jul 06 '16 at 22:34
9

By using reflection you can retrieve the property infos from an object

foreach (PropertyInfo prp in obj.GetType().GetProperties()) {
   if (prp.CanRead) {
      object value = prp.GetValue(obj, null);
      string s = value == null ? "" : value.ToString();
      string name = prp.Name;
      ...
   }
} 

The GetProperties method has an overload accepting BindingFlags through which you can determine which kind of property you need, like private/public instance/static.

You can combine them like this

var properties = type.GetProperties(BindingFlags.Public | 
                                    BindingFlags.NonPublic | 
                                    BindingFlags.Instance);

Applied to your problem you could write

List<Person> people = ...;
Type type = typeof(Person);
PropertyInfo[] properties = type.GetProperties();
var sb = new StringBuilder();

// First line contains field names
foreach (PropertyInfo prp in properties) {
   if (prp.CanRead) {
      sb.Append(prp.Name).Append(';');
   }
}
sb.Length--; // Remove last ";"
sb.AppendLine();

foreach (Person person in people) {
    foreach (PropertyInfo prp in properties) {
       if (prp.CanRead) {
          sb.Append(prp.GetValue(person, null)).Append(';');
       }
    }
    sb.Length--; // Remove last ";"
    sb.AppendLine();
}

File.AppendAllText("C:\Data\Persons.csv", sb.ToString());

It is also a good idea to enclose strings in double quotes and to escape doubles quotes they contain by doubling them.

Olivier Jacot-Descombes
  • 93,432
  • 11
  • 126
  • 171
2

you can use something like this:

...
        PropertyInfo[] properties = obj.GetType().GetProperties();
        string CSVRow = "";
        foreach (PropertyInfo pi in properties)
        {
            CSVRow = CSVRow + pi.GetValue(obj, null) + ";";
        }
        CSVRow.Remove(CSVRow.Length - 1, 1);
...
M. X
  • 1,287
  • 4
  • 19
  • 31
2

I believe FileHelpers is good for this, I've not used it in anger myself though

Liath
  • 9,442
  • 9
  • 49
  • 80
0

Something like this:

public string ToCsv()
{
    return string.Join(";", new string[]{
        _firstName,
        _lastName,
        _age.ToString()
    }.Select(str=>Escape(str)));
}

Or, using reflection,

public static string ToCsv(this object obj)
{
    return string.Join(";",
        this.GetType().GetProperties().Select(pi=>
            Escape(pi.GetValue(this, null).ToString())
        ));
}

Where Escape is an appropriate escaping function.

Thom Smith
  • 13,599
  • 6
  • 43
  • 83
0

One possible implementation for you, that read complex objects (deep object serialization), like array of objects with properties that are array of objects, and saves o a string formatted like CSV file:

private string ToCsv(string separator, IEnumerable<object> objectList)
{
    StringBuilder csvData = new StringBuilder();
    foreach (var obj in objectList)
    {
        csvData.AppendLine(ToCsvFields(separator, obj));
    }
    return csvData.ToString();
}

private string ToCsvFields(string separator, object obj)
{
    var fields = obj.GetType().GetProperties();
    StringBuilder line = new StringBuilder();

    if (obj is string)
    {
        line.Append(obj as string);
        return line.ToString();
    }

    foreach (var field in fields)
    {
        var value = field.GetValue(obj);
        var fieldType = field.GetValue(obj).GetType();

        if (line.Length > 0)
        {
            line.Append(separator);
        }
        if (value == null)
        {
            line.Append("NULL");
        }
        if (value is string)
        {
            line.Append(value as string);
        }
        if (typeof(IEnumerable).IsAssignableFrom(fieldType))
        {
            var objectList = value as IEnumerable;
            StringBuilder row = new StringBuilder();

            foreach (var item in objectList)
            {
                if (row.Length > 0)
                {
                    row.Append(separator);
                }
                row.Append(ToCsvFields(separator, item));
            }
            line.Append(row.ToString());
        }
        else
        {
            line.Append(value.ToString());
        }
    }
    return line.ToString();
}
Lombas
  • 860
  • 1
  • 7
  • 24