使用CsvHelper时进行十进制转换。

6
当我尝试使用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 成员类型:System.Decimal\n 类型转换器:'CsvHelper.TypeConversion.DecimalConverter'\nIReader 状态:\n 列数:0\n 当前索引: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

2个回答

7
所遇到的问题是 CSVHelper 不知道如何将空字段转换为十进制值,有两种选择可以使用:
  1. 更新 CSV 文件以向空字段添加默认值(即将 LensBespokePrice 的默认值设置为 0)。
  2. 创建一种类型转换来处理将空单元格转换为十进制数。
如果您能够修改 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,

否则,您需要为空的十进制数和布尔值都使用类型转换器。例如,如果您的所有代码都在一个文件中,可能如下所示:

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结果。
我添加了两个自定义转换器(CustomBooleanConverterCustomDecimalConverter)。然后更新了payer类,添加了属性到LensBespokePriceCareOnly属性。此外,CareOnly上没有false值的属性,尽管这不是必需的,但是是一种好的实践。
澄清为什么这仅发生在foreach循环中而不是var records = csv.GetRecords<Payer>();是因为直到记录被枚举之前,这些值才会转换为您的payer类。

谢谢,现在一切都正常了!我有点难以理解错误输出,但是现在我已经有了一个例子,就清楚多了。 - user2792697
有趣。我有相同的设置,但在这个[BooleanTrueValues("Y")] [BooleanFalseValues("N")] public bool UseName { get; set; }上遇到了TypeConverter ERROR: 'CsvHelper.TypeConversion.BooleanConverter'错误。 - T.S.
是的,在那里存在一个错误,这些属性在这种情况下并不真正起作用。https://github.com/JoshClose/CsvHelper/issues/1907#issue-1055787326 - T.S.

0

Payer(付款人)是一整个值类。在控制台输出时,它看起来像是在尝试转换。我认为你需要告诉它你想要打印的支付人部分,就像这样:

Console.WriteLine(payer.surname);

虽然这是绝对正确的,但问题在于尝试将空值转换为十进制。 - JHizzal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接