使用 CsvHelper 时转换为十进制
Posted
技术标签:
【中文标题】使用 CsvHelper 时转换为十进制【英文标题】:Convert to decimal when using CsvHelper 【发布时间】:2021-12-01 11:13:44 【问题描述】:当我尝试使用 CsvHelper 解析以下 CSV 时,我收到“无法执行转换”错误(完整错误如下)。看起来我缺少有关如何处理以小数形式读取值的内容。已经看到其他一些与设置文化有关的答案,但这似乎没有帮助。
CSV 数据为:
Title,Amount,NHS,Reference,GoCardless ID,email,surname,firstname,Full Name,DOB,Age,Right Lens,Left Lens,RightLensMonthlyAmount,LeftLensMonthlyAmount,LensMonthlyAmount,FeeMonthlyAmount,VAT Basis,LensBespokePrice,CareOnly,Notes
Mrs,24.3,N,100247,CUXXX,email@gmail.com,User,Test,Test User,17/09/1957,64,DAILIES® AquaComfort PLUS 30 Pack,DAILIES® AquaComfort PLUS 30 Pack,16.5,16.5,33,6.35,,,,
将此数据映射到属性的类是:
public class Payer
public string Title get; set;
public decimal Amount get; set;
[BooleanTrueValues("Y")]
[BooleanFalseValues("N")]
public bool NHS get; set;
public string Reference get; set;
[Name("GoCardless ID")]
public string GoCardless_ID get; set;
public string email get; set;
public string surname get; set;
public string firstname get; set;
[Name("Full Name")]
public string Fullname get; set;
[Name("DOB")]
public string Dob get; set;
public int Age get; set;
[Name("Right Lens")]
public string RightLens get; set;
[Name("Left Lens")]
public string LeftLens get; set;
public decimal RightLensMonthlyAmount get; set;
public decimal LeftLensMonthlyAmount get; set;
public decimal LensMonthlyAmount get; set;
public decimal FeeMonthlyAmount get; set;
[Name("VAT Basis")]
public string VATBasis get; set;
public decimal LensBespokePrice get; set;
[BooleanTrueValues("Y")]
public bool CareOnly get; set;
我与解析 CSV 相关的代码是:
static void Main(string[] args)
var culture = new CultureInfo("en-GB");
var config = new CsvHelper.Configuration.CsvConfiguration(culture);
using (var reader = new StreamReader("test.csv"))
using (var csv = new CsvReader(reader, config))
var records = csv.GetRecords<Payer>();
Console.WriteLine("Got records"); //this prints on the console
foreach (var payer in records)
Console.WriteLine(payer);
该错误仅发生在 foreach
循环中,而不是实际的 GetRecords()
方法。
完全错误:
CsvHelper.TypeConversion.TypeConverterException: "无法执行转换。\n 文本: ''\n MemberType: System.Decimal\n TypeConverter: 'CsvHelper.TypeConversion.DecimalConverter'\nIReader 状态:\n ColumnCount: 0\ n CurrentIndex: 18\n HeaderRecord:\n["Title","Amount","NHS","Reference","GoCardless ID","email","surname","firstname","Full Name"," DOB","Age","Right Lens","Left Lens","RightLensMonthlyAmount","LeftLensMonthlyAmount","LensMonthlyAmount","FeeMonthlyAmount","增值税基础","LensBespokePrice","CareOnly","Notes"] \nIParser 状态:\n ByteCount: 0\n CharCount: 392\n Row: 2\n RawRow: 2\n Count: 21\n RawRecord:\nMrs,24.3,N,100247,CUXXX,email@gmail.com,用户,测试,测试用户,17/09/1957,64,DAILIES® AquaComfort PLUS 30 包,DAILIES® AquaComfort PLUS 30 包,16.5,16.5,33,6.35,,,,\r\n\n" 在 CsvHelper.TypeConversion.DefaultTypeConverter.ConvertFromString(字符串文本,IReaderRow 行,MemberMapData memberMapData)\n 在 CsvHelper.TypeConversion.DecimalConverter.ConvertFromString(字符串文本,IReaderRow 行,MemberMapData memberMapData)\n 在 CsvHelper.Expressions.RecordCreator.CreateT\n在 CsvHelper.Expressions.RecordManager.CreateT\n 在 CsvHelper.CsvReader.d__87`1.MoveNext()\n 在 /Users/abhi/Documents/Practice/dd-journal/ 中的 dd_journal.Program.Main(String[] args) Program.cs:22
【问题讨论】:
【参考方案1】:所以问题在于 CSVHelper 不了解如何将 LensBespokePrice
的空字段转换为十进制值。您可以在这里使用两个选项:
-
更新 CSV 文件以向空字段添加默认值(即 LensBespokePrice 为 0)。
创建类型转换以将空单元格处理为小数。
您可以修改 CSV 文件吗?如果是这样,那么 #1 通过将 CSV 更改为(注意 LensBespokePrice
和 CareOnly
的更改)来工作:
Title,Amount,NHS,Reference,GoCardless ID,email,surname,firstname,Full Name,DOB,Age,Right Lens,Left Lens,RightLensMonthlyAmount,LeftLensMonthlyAmount,LensMonthlyAmount,FeeMonthlyAmount,VAT Basis,LensBespokePrice,CareOnly,Notes
Mrs,24.3,N,100247,CUXXX,email@gmail.com,User,Test,Test User,17/09/1957,64,DAILIES® AquaComfort PLUS 30 Pack,DAILIES® AquaComfort PLUS 30 Pack,16.5,16.5,33,6.35,,0,N,
如果没有,您将需要 type converter 用于空小数和空布尔值。例如,将所有代码放在一个文件中,可能如下所示:
using CsvHelper;
using CsvHelper.Configuration;
using CsvHelper.Configuration.Attributes;
using CsvHelper.TypeConversion;
using System;
using System.Globalization;
using System.IO;
using System.Text.Json;
namespace ConsoleApp1
class Program
static void Main(string[] args)
var culture = new CultureInfo("en-GB");
var config = new CsvConfiguration(culture);
using (var reader = new StreamReader("test.csv"))
using (var csv = new CsvReader(reader, config))
var records = csv.GetRecords<Payer>();
Console.WriteLine("Got records"); //this prints on the console
foreach (var payer in records)
var j = JsonSerializer.Serialize(payer);
Console.WriteLine(j);
public class CustomDecimalConverter : DecimalConverter
public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
if(decimal.TryParse(text, out var result))
return result;
else
return decimal.Zero;
public class CustomBooleanConverter : BooleanConverter
public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
if (bool.TryParse(text, out var result))
return result;
else
return false;
public class Payer
public string Title get; set;
public decimal Amount get; set;
[BooleanTrueValues("Y")]
[BooleanFalseValues("N")]
public bool NHS get; set;
public string Reference get; set;
[Name("GoCardless ID")]
public string GoCardless_ID get; set;
public string email get; set;
public string surname get; set;
public string firstname get; set;
[Name("Full Name")]
public string Fullname get; set;
[Name("DOB")]
public string Dob get; set;
public int Age get; set;
[Name("Right Lens")]
public string RightLens get; set;
[Name("Left Lens")]
public string LeftLens get; set;
public decimal RightLensMonthlyAmount get; set;
public decimal LeftLensMonthlyAmount get; set;
public decimal LensMonthlyAmount get; set;
public decimal FeeMonthlyAmount get; set;
[Name("VAT Basis")]
public string VATBasis get; set;
[TypeConverter(typeof(CustomDecimalConverter))]
public decimal LensBespokePrice get; set;
[BooleanTrueValues("Y")]
[BooleanFalseValues("N")]
[TypeConverter(typeof(CustomBooleanConverter))]
public bool CareOnly get; set;
请注意,我在
Main
方法内的foreach
循环中添加了JsonSerializer.Serialize(payer);
,以便您可以从控制台查看JSON 结果。
我添加了两个自定义转换器(CustomBooleanConverter
和 CustomDecimalConverter
)。然后更新payer
类以将属性添加到LensBespokePrice
和CareOnly
属性。此外,CareOnly
上没有用于 false 值的属性,虽然不是必需的,但这是一种很好的做法。
澄清为什么这只发生在您的foreach
循环而不是var records = csv.GetRecords<Payer>();
是因为在枚举记录之前这些值实际上并未转换为您的payer
类。
【讨论】:
谢谢,现在一切正常!我有点费力地理解错误输出,但现在我已经有了一个例子,现在更清楚了。【参考方案2】:Payer 是一整类值。当您写入控制台时,它似乎正在尝试转换。我相信您需要告诉它您想要打印的付款人的哪一部分:
Console.WriteLine(payer.surname);
【讨论】:
虽然这绝对是正确的,但问题在于尝试将空值转换为小数。以上是关于使用 CsvHelper 时转换为十进制的主要内容,如果未能解决你的问题,请参考以下文章
当十进制为 0 时,DecimalFormat.parse() 的转换结果时发生 ClassCastException