I'm writing my ClassMap which works for my first basic fields (including those with column name not matching class member). But I have 2 fields which need a particular work
1) I have a color stored as string. I need some code which convert the input to 2 values and store each one in a specific member.
2) I have an ID which match CSV item ID (that's the father or mother ID). But I need to convert it to the ID in my database (so I have to write some code to match CSV_ID to DB_ID).
Is it possible to add this custom logic with CSVHelper ? Thanks for help. Vincent
As it seems, CSVHelper also supports the same type converter injection during class mapping.
https://joshclose.github.io/CsvHelper/examples/configuration/class-maps/type-conversion
By combining this with the support for mapping by alternate names,
https://joshclose.github.io/CsvHelper/examples/configuration/class-maps/mapping-by-alternate-names
Having a csv file like this:
Id,Name,Color
1,OGUZ OZGUL,#f0f0f0
2,VINCENT,#80A0C0
3,OZGUL OGUZ,#00A000
it is possible to achieve what's needed as follows:
using System;
using System.Globalization;
using System.IO;
using CsvHelper;
using CsvHelper.Configuration;
using CsvHelper.TypeConversion;
using System.Linq;
namespace console
{
public class Program
{
public class Foo
{
// Represents the database Id
public int Id { get; set; }
public string Name { get; set; }
// Represents a three character color code, like #FFF
public string Color3 { get; set; }
// Represents a six character color code like #FFFFFF
public string Color6 { get; set; }
}
// OK, we are not converting between types here, but who cares?
// CSVHelper certainly doesn't.
public class IdConverter : DefaultTypeConverter
{
public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
{
string csvId = text;
int databaseId = Convert.ToInt32(text) + 10000;
return databaseId;
}
public override string ConvertToString(object value, IWriterRow row, MemberMapData memberMapData)
{
return ((int)value - 10000).ToString();
}
}
// Again, we are changing the value as we wish, not the type.
public class Color3Converter : DefaultTypeConverter
{
public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
{
// format: #ffffff
return "#" + text[1] + text[3] + text[5];
}
public override string ConvertToString(object value, IWriterRow row, MemberMapData memberMapData)
{
// format: #fff
return "#" + ((string)value)[1] + "0" + ((string)value)[2] + "0" + ((string)value)[3] + "0";
}
}
// By combining a type converter and alternative name
// we achieve one CSV field value to be mapped to two properties
// of our class Foo
public sealed class FooMap : ClassMap<Foo>
{
public FooMap()
{
Map(m => m.Id).TypeConverter<IdConverter>();
Map(m => m.Name);
Map(m => m.Color3).TypeConverter<Color3Converter>().Name("Color");
Map(m => m.Color6).Name("Color");
}
}
static void Main(string[] args)
{
using (var reader = new StreamReader("data.csv"))
{
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
csv.Configuration.RegisterClassMap<FooMap>();
Foo[] records = csv.GetRecords<Foo>().ToArray();
foreach(Foo record in records)
{
Console.WriteLine
(
"Foo, Id: {0}, Name: {1}, Color3: {2}, Color6: {3}",
record.Id,
record.Name,
record.Color3,
record.Color6
);
}
}
}
}
}
}
The output of the program is:
Foo, Id: 10001, Name: OGUZ OZGUL, Color3: #fff, Color6: #f0f0f0
Foo, Id: 10002, Name: VINCENT, Color3: #8AC, Color6: #80A0C0
Foo, Id: 10003, Name: OZGUL OGUZ, Color3: #0A0, Color6: #00A000