如何将 EnumConverter 与 CsvHelper 一起使用
Posted
技术标签:
【中文标题】如何将 EnumConverter 与 CsvHelper 一起使用【英文标题】:How to use EnumConverter with CsvHelper 【发布时间】:2015-10-18 10:54:34 【问题描述】:我正在使用CsvHelper 将一个类序列化为 csv 文件 - 直到这里一切正常。
现在我正在尝试找到一种方法将类的枚举属性转换为它们在 csv 中的 int 值,以便稍后使用 CSV 进行批量插入。
我在 CsvHelper 中找到了 EnumConverter 类,但我不知道如何正确使用它,因为我所有的尝试都失败了。
这是我的映射类代码
public sealed class MyMapping : CsvClassMap<TradingCalendarException>
public MyMapping()
EnumConverter enumConverter = new EnumConverter(typeof(CalendarExceptionEntityType));
Map(m => m.ExceptionEntityType).Index(0).Name("EXCEPTION_ENTITY_TYPE").TypeConverter(enumConverter);
Map(m => m.ExceptionEntityIdentifier).Index(1).Name("EXCEPTION_ENTITY_IDENTIFIER");
Map(m => m.OptionType).Index(2).Name("OPTION_TYPE");
Map(m => m.StartDatetime).Index(3).Name("EXCEPTION_START_DATETIME");
Map(m => m.EndDatetime).Index(4).Name("EXCEPTION_END_DATETIME");
Map(m => m.DataSourceType).Index(5).Name("DATA_SOURCE_TYPE");
Map(m => m.Description).Index(6).Name("DESCRIPTION");
写作部分
using (StreamWriter file = new StreamWriter(filePath, false, Encoding.UTF8))
CsvWriter writer = new CsvWriter(file);
MyMapping mapping = new MyMapping();
writer.Configuration.RegisterClassMap(mapping);
writer.WriteRecords(calendarExceptionList);
映射的其余部分(索引和命名)正在工作,只是 EnumConverter 没有做任何更改。
我没有在网上找到任何示例。
谢谢!
【问题讨论】:
【参考方案1】:这是我提出的解决方案:
public class CalendarExceptionEnumConverter<T> : DefaultTypeConverter where T : struct
public override string ConvertToString(TypeConverterOptions options, object value)
T result;
if(Enum.TryParse<T>(value.ToString(),out result))
return (Convert.ToInt32(result)).ToString();
throw new InvalidCastException(String.Format("Invalid value to EnumConverter. Type: 0 Value: 1",typeof(T),value));
并将其用作以下内容:
Map(m => m.ExceptionEntityType).TypeConverter<CalendarExceptionEnumConverter<CalendarExceptionEntityType>>();
【讨论】:
我使用了这个解决方案,现在只有这个属性被写入 csv 文件,我的对象上的所有其他属性都被忽略了。什么给了? 好的,找到了。我删除了所有属性,只留下了“ClassMap”。显然,当周围有映射器时,CsvHelper 会忽略属性。【参考方案2】:我使用了 Yarimi 的解决方案,但发现它无法从 .csv 中读取枚举值(可以写入)
我的解决方案是让类从 EnumTypeConverter 扩展,而不是 DefaultTypeConverter。
这是完整的代码
public class OurEnumConverter<T> : CsvHelper.TypeConversion.EnumConverter where T : struct
public OurEnumConverter(): base(typeof(T))
public override string ConvertToString(CsvHelper.TypeConversion.TypeConverterOptions options, object value)
T result;
if (Enum.TryParse<T>(value.ToString(), out result))
return (Convert.ToInt32(result)).ToString();
return base.ConvertToString(options, value);
//throw new InvalidCastException(String.Format("Invalid value to EnumConverter. Type: 0 Value: 1", typeof (T), value));
public override object ConvertFromString(TypeConverterOptions options, string text)
int parsedValue;
//System.Diagnostics.Debug.WriteLine($"typeof(T).Name = text");
if (Int32.TryParse(text, out parsedValue))
return (T)(object)parsedValue;
return base.ConvertFromString(options, text);
//throw new InvalidCastException(String.Format("Invalid value to EnumConverter. Type: 0 Value: 1", typeof(T), text));
这是它的使用方法
public class TickTradeClassMap : CsvHelper.Configuration.CsvClassMap<TickData.TickTrade>
public TickTradeClassMap()
Map(m => m.price);
Map(m => m.size);
Map(m => m.exchange).TypeConverter<OurEnumConverter<ATExchangeEnum>>();
Map(m => m.condition1).TypeConverter<OurEnumConverter<ATTradeConditionEnum>>();
【讨论】:
这不再适用于最新的 CsvHelper,因为 ITypeConverter 接口已更改为支持更多上下文信息。 我认为它也比它必须的复杂得多......但我不怪你,因为在 C# 7.3 之前处理枚举是多么烦人【参考方案3】:这就是我为最新版本的CSV Helper
(7.1.1
)所做的:
public class AggregateEnumConverter<T> : EnumConverter where T : struct
public AggregateEnumConverter() : base(typeof(T))
public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
if(!Enum.TryParse(text, out AggregateType aggregateType))
// This is just to make the user life simpler...
if(text == "24HAVG")
return AggregateType._24HAVG;
// If an invalid value is found in the CSV for the Aggregate column, throw an exception...
throw new InvalidCastException($"Invalid value to EnumConverter. Type: typeof(T) Value: text");
return aggregateType;
注意:上面的代码使用了 C# 7 新的内联输出变量。更多信息在这里:How should I convert a string to an enum in C#?
这就是你如何使用自定义EnumConverter
:
/// <summary>
/// Maps Tag class properties to the CSV columns' names
/// </summary>
public sealed class TagMap : ClassMap<Tag>
public TagMap(ILogger<CsvImporter> logger)
Map(tag => tag.Aggregate).Name("aggregate").TypeConverter<AggregateEnumConverter<AggregateType>>();
【讨论】:
【参考方案4】:将int
属性添加到您的TradingCalendarException
类,该类来回转换为您的自定义枚举CalendarExceptionEntityType
,例如:
public int ExceptionEntityTypeInt
get return (int)ExceptionEntityType;
set ExceptionEntityType = (CalendarExceptionEntityType)value;
使用Map(m => m.ExceptionEntityTypeInt).Index(0).Name("EXCEPTION_ENTITY_TYPE_INT")
代替您的枚举转换器Map(m => m.ExceptionEntityType).Index(0).Name("EXCEPTION_ENTITY_TYPE").TypeConverter(new MyMapping())
【讨论】:
嘿,问题是(我忘了提)TradingCalendarException 模型不是我的,我无法更改它。 将该属性添加到您的包含 TradingCalendarException 的自定义类型中。制作一个适配器并使用它。以上是关于如何将 EnumConverter 与 CsvHelper 一起使用的主要内容,如果未能解决你的问题,请参考以下文章
Swinject:如何将委托模式与接口隔离(类与接口)一起使用?