Describe the bug
I have a custom Option<T> type that I use to represent optional types as a replacement to nullable types. This type also implements IEnumerable<T>. In order to convert fields of that type, I have written a custom ITypeConverterFactory and registered it in the CsvContext. However, when I try to use GetField<Option<int>> for example, the program fails with the following exception:
CsvHelper.TypeConversion.TypeConverterException : Converting IEnumerable types is not supported for a single field. If you want to do this, create your own ITypeConverter and register it in the TypeConverterFactory by calling AddConverter.
To Reproduce
The csv file looks like this:
Integer,String
,bar
1,
public readonly record struct Option<T> : IEnumerable<T>
{
private readonly T _value;
internal Option(T value)
{
IsPresent = true;
_value = value;
}
public bool IsPresent { get; }
}
public class CsvOptionConverter<T> : DefaultTypeConverter
{
private readonly ITypeConverter _wrappedTypeConverter;
public CsvOptionConverter(TypeConverterCache typeConverterCache)
{
_wrappedTypeConverter = typeConverterCache.GetConverter(typeof(T));
}
public override object? ConvertFromString(string? text, IReaderRow row, MemberMapData memberMapData) =>
GetOptionFromString(text, row, memberMapData);
private Option<T> GetOptionFromString(string? text, IReaderRow row, MemberMapData memberMapData)
{
if (string.IsNullOrEmpty(text) || memberMapData.TypeConverterOptions.NullValues.Contains(text))
{
return new Option<T>();
}
return new Option<T>((T)_wrappedTypeConverter.ConvertFromString(text, row, memberMapData));
}
}
public class CsvOptionConverterFactory : ITypeConverterFactory
{
public bool CanCreate(Type type) =>
type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Option<>);
public bool Create(Type type, TypeConverterCache cache, out ITypeConverter typeConverter)
{
var wrappedType = type.GetGenericArguments()[0];
var converterType = typeof(CsvOptionConverter<>).MakeGenericType(wrappedType);
typeConverter = (Activator.CreateInstance(converterType, cache) as ITypeConverter)!;
return true;
}
}
using var reader = new StreamReader(...);
using var csv = new CsvReader(reader, _csvConfiguration);
csv.Context.TypeConverterCache.AddConverterFactory(new CsvOptionConverterFactory());
csv.Read();
csv.ReadHeader();
while (csv.Read())
{
Console.WriteLine($"Integer: {csv.GetField<Option<int>>("Integer")}");
Console.WriteLine($"String: {csv.GetField<Option<string>>("String")}");
}
Expected behavior
The csv gets parsed correctly and does not throw any exceptions.
I just ran into this issue (for same reason). Registered TypeConverterFactory is not consulted for types implementing IEnumerable<T>, i.e. CanCreate(...) does not get called.
Describe the bug I have a custom
Option<T>
type that I use to represent optional types as a replacement to nullable types. This type also implementsIEnumerable<T>
. In order to convert fields of that type, I have written a customITypeConverterFactory
and registered it in theCsvContext
. However, when I try to useGetField<Option<int>>
for example, the program fails with the following exception:To Reproduce The csv file looks like this:
Expected behavior The csv gets parsed correctly and does not throw any exceptions.