使用 CsvHelper 时转换为十进制
Convert to decimal when using CsvHelper
当我尝试使用 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,,,,
我的 class 将此数据映射到属性是:
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: "The conversion cannot be performed.\n Text: ''\n MemberType: System.Decimal\n TypeConverter: 'CsvHelper.TypeConversion.DecimalConverter'\nIReader state:\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","VAT Basis","LensBespokePrice","CareOnly","Notes"]\nIParser state:\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,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,,,,\r\n\n"
at CsvHelper.TypeConversion.DefaultTypeConverter.ConvertFromString(String text, IReaderRow row, MemberMapData memberMapData)\n at CsvHelper.TypeConversion.DecimalConverter.ConvertFromString(String text, IReaderRow row, MemberMapData memberMapData)\n at CsvHelper.Expressions.RecordCreator.CreateT\n at CsvHelper.Expressions.RecordManager.CreateT\n at CsvHelper.CsvReader.d__87`1.MoveNext()\n at dd_journal.Program.Main(String[] args) in /Users/abhi/Documents/Practice/dd-journal/Program.cs:22
Payer 是一个完整的 class 值。看起来它在您写入控制台时正在尝试转换。我相信你需要告诉它你想要打印付款人的哪一部分:
Console.WriteLine(payer.surname);
所以问题是 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; }
}
}
Please note that I added the JsonSerializer.Serialize(payer);
in the foreach
loop within the Main
method so that you can view the JSON result from the console.
我添加了两个自定义转换器(CustomBooleanConverter
和 CustomDecimalConverter
)。然后更新 payer
class 以将属性添加到 LensBespokePrice
和 CareOnly
属性。此外,您在 CareOnly
上没有用于假值的属性,虽然不需要,但这是一个很好的做法。
澄清为什么这只发生在你的 foreach
循环而不是 var records = csv.GetRecords<Payer>();
是因为这些值实际上并没有转换成你的 payer
class 直到记录被枚举。
当我尝试使用 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,,,,
我的 class 将此数据映射到属性是:
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: "The conversion cannot be performed.\n Text: ''\n MemberType: System.Decimal\n TypeConverter: 'CsvHelper.TypeConversion.DecimalConverter'\nIReader state:\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","VAT Basis","LensBespokePrice","CareOnly","Notes"]\nIParser state:\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,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,,,,\r\n\n" at CsvHelper.TypeConversion.DefaultTypeConverter.ConvertFromString(String text, IReaderRow row, MemberMapData memberMapData)\n at CsvHelper.TypeConversion.DecimalConverter.ConvertFromString(String text, IReaderRow row, MemberMapData memberMapData)\n at CsvHelper.Expressions.RecordCreator.CreateT\n at CsvHelper.Expressions.RecordManager.CreateT\n at CsvHelper.CsvReader.d__87`1.MoveNext()\n at dd_journal.Program.Main(String[] args) in /Users/abhi/Documents/Practice/dd-journal/Program.cs:22
Payer 是一个完整的 class 值。看起来它在您写入控制台时正在尝试转换。我相信你需要告诉它你想要打印付款人的哪一部分:
Console.WriteLine(payer.surname);
所以问题是 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; }
}
}
Please note that I added the
JsonSerializer.Serialize(payer);
in theforeach
loop within theMain
method so that you can view the JSON result from the console.
我添加了两个自定义转换器(CustomBooleanConverter
和 CustomDecimalConverter
)。然后更新 payer
class 以将属性添加到 LensBespokePrice
和 CareOnly
属性。此外,您在 CareOnly
上没有用于假值的属性,虽然不需要,但这是一个很好的做法。
澄清为什么这只发生在你的 foreach
循环而不是 var records = csv.GetRecords<Payer>();
是因为这些值实际上并没有转换成你的 payer
class 直到记录被枚举。