Skip to content

Instantly share code, notes, and snippets.

@MaciejLisCK
Last active June 29, 2020 05:56
Show Gist options
  • Save MaciejLisCK/8804658 to your computer and use it in GitHub Desktop.
Save MaciejLisCK/8804658 to your computer and use it in GitHub Desktop.
C# CSV Builder using lambda (with escape)
class Program
{
static void Main(string[] args)
{
var csvBuilder = new CsvBuilder<User>();
var users = new User[]
{
new User() { FirstName = "Maciej", LastName = "Lis", BirthDate = new DateTime(1986,10,31) },
new User() { FirstName = "Krzysztof", LastName = "Lis", BirthDate = new DateTime(1960,1,1), Notes = "Like \".NET Framework\"."},
new User() { FirstName = "Barbara", LastName = "Lis", BirthDate = new DateTime(1960,2,12), Notes = "Buy:\n- 1 orange\n- milk" },
};
var csv = csvBuilder
.Build(users,
u => u.LastName,
u => u.BirthDate.ToShortDateString(),
u => u.Notes);
var csvWithHeader = csvBuilder
.Build(users,
new [] {"Last Name", "Birth Date", "Notes"},
u => u.LastName,
u => u.BirthDate.ToShortDateString(),
u => u.Notes);
Console.WriteLine(csv);
Console.WriteLine();
Console.WriteLine(csvWithHeader);
Console.ReadKey();
}
}
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
public string Notes { get; set; }
}
public class CsvBuilder<T>
{
protected string[] _escapeIndicators = new[] { ",", "\n", "\"" };
public string Build(IEnumerable<T> objects, params Func<T, string>[] columnsGetters)
{
var csv = new StringBuilder();
foreach (var @object in objects)
{
var values = columnsGetters.Select(cg => cg(@object));
var row = BuildRow(values);
csv.Append(row);
}
return csv.ToString();
}
public string Build(IEnumerable<T> objects, IEnumerable<string> columnsNames, params Func<T, string>[] columnsGetters)
{
bool isHeadersAndColumnsLengthEqual = columnsNames.Count() == columnsGetters.Length;
if (!isHeadersAndColumnsLengthEqual)
throw new ArgumentException("Header length and columns length are not equal.");
var header = BuildRow(columnsNames);
var values = Build(objects, columnsGetters);
var headerWithValues = header + values;
return headerWithValues;
}
protected string BuildRow(IEnumerable<string> values)
{
var row = new StringBuilder();
foreach (var value in values)
{
var safeValue = value ?? String.Empty;
if (HasToEscape(safeValue))
safeValue = EscapeValue(safeValue);
row.Append(safeValue);
row.Append(",");
}
row.Length -= 1;
row.AppendLine();
return row.ToString();
}
protected bool HasToEscape(string value)
{
return _escapeIndicators.Any(i => value.Contains(i));
}
protected string EscapeValue(string value)
{
value = value.Replace("\"", "\"\"");
value = "\"" + value + "\"";
return value;
}
}
Lis,1986-10-31,
Lis,1960-01-01,"Like "".NET Framework""."
Lis,1960-02-12,"Buy:
- 1 orange
- milk"
Last Name,Birth Date,Notes
Lis,1986-10-31,
Lis,1960-01-01,"Like "".NET Framework""."
Lis,1960-02-12,"Buy:
- 1 orange
- milk"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment