Problem :
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
Solution :
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
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:DataPersons.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.
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);
...
I believe FileHelpers is good for this, I’ve not used it in anger myself though
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.
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();
}