在CsvHelper中添加可检测的Nullable值

17
我在想CsvHelper这个库中是否有任何配置我漏掉了以将值转换为null。我非常喜欢这个库,但我一直认为应该有某种配置来让它知道您的文件中表示NULL的值是什么。例如,一个列的值为“NA”,“EMPTY”,“NULL”等。我确定我可以创建自己的TypeConverter,但我希望在配置中设置一个更容易的选项,因为我遇到的文件通常都会出现这种情况。
是否有一个相对简单的配置设置可以实现这个功能?
我发现CsvHelper.TypeConversion命名空间中的TypeConversion,但不确定在哪里应用这样的东西或正确用法的示例:
new NullableConverter(typeof(string)).ConvertFromString(new TypeConverterOptions(), "NA")

我也在使用最新的版本2.2.2

谢谢!


4
目前您需要像@JNYRanger所说一样创建一个自定义转换器并扩展NullableConverter。具备设置其他值的配置,使其被视为"空值"的能力似乎是一个很方便的功能,我很惊讶以前没有这个需求。我会将其作为一个新的功能请求添加进去。 - Josh Close
@JoshClose,甚至在地图上添加一个“PrepareData”事件也可以,例如Map(m => m.Prop).Prepare = func<instring,outstring>,我们可以使用convertusing来实现这一点,但那会完全覆盖自动映射。 - b_levitt
4个回答

37

我认为自从这个问题被提出七年以来的十三个版本里,不需要自定义类型映射类完成此操作的选项有所增加,例如:

csvReader.Context.TypeConverterOptionsCache.GetOptions<string>().NullValues.Add("NULL");
csvReader.Context.TypeConverterOptionsCache.GetOptions<DateTime?>().NullValues.AddRange(new[] { "NULL", "0" });
csvReader.Context.TypeConverterOptionsCache.GetOptions<int?>().NullValues.Add("NULL");
csvReader.Context.TypeConverterOptionsCache.GetOptions<bool>().BooleanFalseValues.Add("0");
csvReader.Context.TypeConverterOptionsCache.GetOptions<bool>().BooleanTrueValues.Add("1");

8
对于2021年(v22.1.2)初次接触CsvHelper的用户来说,TypeConverterOptionsCache已经不再位于Configuration属性中了。现在它的位置是:csvReader.Context.TypeConverterOptionsCache.GetOptions<>。不确定何时更改,但希望能对大家有所帮助 :) - S. Rasmussen
1
感谢S. Rasmussen,我已经验证并更新了这篇文章。 - Lee Richardson

15

CsvHelper 绝对可以处理可空类型。如果空列被视为 null,您不需要自己编写 TypeConverter。在我的示例中,我假设您正在使用用户定义的 fluent 映射。

首先,您需要构造一个 CsvHelper.TypeConverter 对象来处理 Nullable 类型。请注意,我将使用 int,因为字符串默认允许 null 值。

public class MyClassMap : CsvClassMap<MyClass>
{
     public override CreateMap()
     {
          CsvHelper.TypeConversion.NullableConverter intNullableConverter = new CsvHelper.TypeConversion.NullableConverter(typeof(int?));

          Map(m => m.number).Index(2).TypeConverter(intNullableConverter);
      }
 }

接下来是在您的CsvReader对象上设置属性以允许空列和自动修剪字段。 个人喜欢通过创建一个CsvConfiguration对象并在构造CsvReader对象之前设置所有设置来完成此操作。

CsvConfiguration csvConfig = new CsvConfiguration();
csvConfig.RegisterClassMap<MyClassMap>();
csvConfig.WillThrowOnMissingField = false;
csvConfig.TrimFields = true;

然后,您可以调用myReader = new CsvReader(stream, csvConfig)来构建CsvReader对象。

如果您需要为空值定义已定义的值,例如"NA" == null,则需要自己编写CsvHelper.TypeConversion类。 我建议您扩展NullableConverter类来实现此操作,并覆盖构造函数和ConvertFromString方法。 不过,使用空白值作为空值确实是最好的选择。


3
我使用了 "ConvertUsing" ...
public class RecordMap : CsvHelper.Configuration.ClassMap<Record>
{
    public RecordMap()
    {
        AutoMap();
        Map(m => m.TransactionDate).ConvertUsing( NullDateTimeParser );
        Map(m => m.DepositDate).ConvertUsing( NullDateTimeParser );
    }

    public DateTime? NullDateTimeParser(IReaderRow row)
    {
        //"CurrentIndex" is a bit of a misnomer here - it's the index of the LAST GetField call so we need to +1
        //https://github.com/JoshClose/CsvHelper/issues/1168

        var rawValue = row.GetField(row.Context.CurrentIndex+1);

        if (rawValue == "NULL")
            return null;
        else
            return DateTime.Parse(rawValue);

    }
}

0
现在有一种内置的方法可以通过直接在属性上添加属性来处理这个问题。NullValuesAttribute接受一个字符串列表,这些字符串将被映射为null值。
    [NullValues("NULL", "0001/01/01")]
    public DateTime? BoardDate { get; init; }

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