public static void SaveCsv<TSource>(
this IEnumerable<TSource> data,
string filePath,
CsvMapping mapping,
Action<TSource, Object> conversion,
char delimiter = ',',
Encoding? textEncoding = null,
IReadOnlyCollection<string?>? columnNames = null
)
<ExtensionAttribute>
Public Shared Sub SaveCsv(Of TSource) (
data As IEnumerable(Of TSource),
filePath As String,
mapping As CsvMapping,
conversion As Action(Of TSource, Object),
Optional delimiter As Char = ","C,
Optional textEncoding As Encoding = Nothing,
Optional columnNames As IReadOnlyCollection(Of String) = Nothing
)
public:
[ExtensionAttribute]
generic<typename TSource>
static void SaveCsv(
IEnumerable<TSource>^ data,
String^ filePath,
CsvMapping^ mapping,
Action<TSource, Object^>^ conversion,
wchar_t delimiter = L',',
Encoding^ textEncoding = nullptr,
IReadOnlyCollection<String^>^ columnNames = nullptr
)
[<ExtensionAttribute>]
static member SaveCsv :
data : IEnumerable<'TSource> *
filePath : string *
mapping : CsvMapping *
conversion : Action<'TSource, Object> *
?delimiter : char *
?textEncoding : Encoding *
?columnNames : IReadOnlyCollection<string>
(* Defaults:
let _delimiter = defaultArg delimiter ','
let _textEncoding = defaultArg textEncoding null
let _columnNames = defaultArg columnNames null
*)
-> unit
A method that fills the content of a TSource instance into the properties of mapping.
conversion is called with each CSV row to be written and it gets the TSource instance and mapping as arguments. mapping is passed to the method as dynamic argument: Inside the conversion method the registered DynamicProperty instances can be used like regular .NET properties, but without IntelliSense ("late binding").
With each call of conversion all DynamicProperty instances in mapping are reset to their DefaultValue.
A collection of column names for the header to be written, or null to use the PropertyNames of mapping as column names.
The collection determines the order in which the columns appear in the CSV file.
The collection will be copied. If the collection contains null values, empty strings, or white space, these are replaced by automatically generated column names. Column names cannot appear twice. By default the comparison is case-sensitive but it will be reset to a case-insensitive comparison if the column names are also unique when treated case-insensitive.
Creates a new CSV file. If the target file already exists, it is truncated and overwritten.
When exchanging CSV data with Excel, the appropriate arguments can be determined with GetExcelArguments.
Object serialization with CSV:
using FolkerKinzel.CsvTools;
using FolkerKinzel.CsvTools.Mappings;
using System.Text;
// A namespace alias helps to avoid name conflicts
// with the converters from System.ComponentModel
using Conv = FolkerKinzel.CsvTools.Mappings.TypeConverters;
namespace Examples;
internal sealed record Pupil(string? Name, string? Subject, DayOfWeek? LessonDay, TimeOnly? LessonBegin);
internal static class ObjectSerializationExample
{
public static void CsvReadWritePupils(string filePath)
{
Pupil[] pupils = [
new("Susi", "Piano", DayOfWeek.Wednesday, new TimeOnly(14, 30)),
new("Carl Czerny", "Piano", DayOfWeek.Thursday, new TimeOnly(15, 15)),
new("Frederic Chopin", "Piano", null, null)
];
// A converter can be reused for more than one DynamicProperty:
Conv::TypeConverter<string?> stringConverter = Conv::StringConverter.CreateNullable();
// Initialize a CsvMapping that maps the data from the CSV-Columns and converts it to the right data type.
// Aliases with wildcards can be used to match the column-headers of the CSV file.
CsvMapping mapping = CsvMappingBuilder
.Create()
.AddProperty("Name", ["*name"], stringConverter)
.AddProperty("Subject", ["*subject", "*fach"], stringConverter)
.AddProperty("LessonDay", ["*day", "*tag"], new Conv::EnumConverter<DayOfWeek>().ToNullableConverter())
.AddProperty("LessonBegin", ["*begin?"], new Conv::TimeOnlyConverter().ToNullableConverter())
.Build();
// Create a CSV-File:
pupils.SaveCsv(filePath,
mapping,
static (pupil, mapping) =>
{
mapping.Name = pupil.Name;
mapping.Subject = pupil.Subject;
mapping.LessonDay = pupil.LessonDay;
mapping.LessonBegin = pupil.LessonBegin;
},
columnNames:
["Unterrichtstag", "Unterrichtsbeginn", "Vollständiger Name", "Unterrichtsfach"]);
Console.WriteLine(File.ReadAllText(filePath));
Console.WriteLine();
// Read the CSV file:
using CsvReader<Pupil> pupilsReader =
CsvConverter.OpenRead<Pupil>(filePath,
mapping,
static mapping => new Pupil(mapping.Name,
mapping.Subject,
mapping.LessonDay,
mapping.LessonBegin));
pupils = [.. pupilsReader];
// Write the results to the Console:
foreach (Pupil pupil in pupils)
{
Console.WriteLine(pupil);
}
}
}
/*
Console output:
Unterrichtstag,Unterrichtsbeginn,Vollständiger Name,Unterrichtsfach
3,14:30:00,Susi,Piano
4,15:15:00,Carl Czerny,Piano
,,Frederic Chopin,Piano
Pupil { Name = Susi, Subject = Piano, LessonDay = Wednesday, LessonBegin = 14:30 }
Pupil { Name = Carl Czerny, Subject = Piano, LessonDay = Thursday, LessonBegin = 15:15 }
Pupil { Name = Frederic Chopin, Subject = Piano, LessonDay = , LessonBegin = }
*/
ArgumentNullException | data, or filePath, or mapping, or conversion is null. |
ArgumentException | filePath is not a valid file path. - or - columnNames is not null and a column name in columnNames occurs twice. |
IOException | I/O error. |