我将出色的CsvHelper库(当前为v12.2.2)用于CSV文件生成,并且我试图添加自己的自定义属性以直接在类中指定特殊格式。
我正在写的记录看起来像这样(尽管集成需要约200个数字字段:
class PayrollRecord {
public int EmployeeID { get; set; }
public decimal RegularPay { get; set; }
public decimal RegularHours { get; set; }
public decimal RegularRate { get; set; }
public decimal OvertimePay { get; set; }
public decimal OvertimeHours { get; set; }
public decimal OvertimeRate { get; set; }
// many many more
}
并且我需要确保薪水写成小数点后两位,小时数写成3,支付率写成4;集成需要此。
我创建了一个附加到类映射的十进制转换器:
using CsvHelper; using CsvHelper.TypeConversion; // convert decimal to the given number of places, and zeros are // emitted as blank. public abstract class MyDecimalConverter : DefaultTypeConverter { protected virtual string getFormat() => ""; public override string ConvertToString(object value, IWriterRow row, MemberMapData memberMapData) { if (value is decimal d) return (d == 0) ? string.Empty : string.Format(getFormat(), d); return base.ConvertToString(value, row, memberMapData); } } public class DollarsConverter : MyDecimalConverter { protected override string getFormat() => "{0:0.00}"; // 2 decimal places } public class HoursConverter : MyDecimalConverter { protected override string getFormat() => "{0:0.000}"; // 3 decimal places } public class PayRateConverter : MyDecimalConverter { protected override string getFormat() => "{0:0.0000}"; // 4 decimal places }
然后创建作家时应用这些:
不能很好地扩展:使用〜200个字段,要使映射内容与实际字段定义保持同步将是一个挑战,我非常希望记录结构进行更改,直到我们确定集成度。CsvWriter Writer = new CsvWriter( /* stuff */ ); var classMap = new DefaultClassMap<PayrollRecord>(); classMap.AutoMap(); classMap.Map(m => m.RegularPay).TypeConverter<DollarsConverter>(); classMap.Map(m => m.RegularHours).TypeConverter<HoursConverter>(); classMap.Map(m => m.RegularRate).TypeConverter<PayRateConverter>(); classMap.Map(m => m.OvertimePay).TypeConverter<DollarsConverter>(); classMap.Map(m => m.OvertimeHours).TypeConverter<HoursConverter>(); classMap.Map(m => m.OvertimeRate).TypeConverter<PayRateConverter>(); // many more Writer.Configuration.RegisterClassMap(classMap); ...
这可以正确地完成所有操作,但是
旁注:可以用[Format("..")]
属性注释每个字段,但是要获得我正在寻找的零抑制,格式字符串是一个三部分的丑陋外观,看起来超级容易出错并且非常繁琐进行更改。
我想创建自己的自定义属性,可以将其应用于每个字段成员以指定此属性,因此它看起来类似于:
// custom attribute public enum NumericType { Dollars, Hours, PayRate }; public class DecimalFormatAttribute : System.Attribute { public NumericType Type { get; } public DecimalFormatAttribute(NumericType t) => Type = t; } // then later class PayrollRecord { [DecimalFormat(NumericType.Dollars)] public decimal RegularPay { get; set; } [DecimalFormat(NumericType.Hours)] public decimal RegularHours { get; set; } [DecimalFormat(NumericType.PayRate)] public decimal RegularRate { get; set; } // etc. }
我遇到的问题是如何将我的自定义属性粘贴到类映射上,我认为代码看起来像这样:
var classMap = new DefaultClassMap<PayrollRecord>(); classMap.AutoMap(); foreach (var prop in typeof(PayrollRecord).GetProperties()) { var myattr = (DecimalFormatAttribute)prop.GetCustomAttribute(typeof(DecimalFormatAttribute)); if (myattr != null) { // prop.Name is the base name of the field // WHAT GOES HERE? } }
我已经忙了好几个小时,却找不到如何完成这项工作。
我正在使用出色的CsvHelper库(当前为v12.2.2)来生成CSV文件,并且我试图添加自己的自定义属性以直接在类中指定特殊格式。记录I'...
而不是您自己的自定义属性,您可以将CsvHelper.Configuration.Attributes.TypeConverterAttribute
应用于模型以指定适当的转换器: