如何将 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 Helper7.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 =&gt; m.ExceptionEntityTypeInt).Index(0).Name("EXCEPTION_ENTITY_TYPE_INT") 代替您的枚举转换器Map(m =&gt; m.ExceptionEntityType).Index(0).Name("EXCEPTION_ENTITY_TYPE").TypeConverter(new MyMapping())

【讨论】:

嘿,问题是(我忘了提)TradingCalendarException 模型不是我的,我无法更改它。 将该属性添加到您的包含 TradingCalendarException 的自定义类型中。制作一个适配器并使用它。

以上是关于如何将 EnumConverter 与 CsvHelper 一起使用的主要内容,如果未能解决你的问题,请参考以下文章

无法将大量数据写入流

JOOQ 使用内联转换器将字符串转换为枚举

PropertyGrid中的枚举显示为中文

Swinject:如何将委托模式与接口隔离(类与接口)一起使用?

如何将 'afterColumnResize' 事件与 'rhandsontable' 一起使用?

如何将 Quickblox 与 angularjs 集成?