如果发生单个读取异常,GetRecord<T> returns null - CSVHelper library .NET
GetRecord<T> returns null if a single reading exception occurs - CSVHelper library .NET
我正在使用 .NET 的 CSVHelper 库
https://github.com/JoshClose/CsvHelper
我有一张 class-map,如下所示:
public class MyMap : ClassMap<Product>
{
Map(c => c.DateVariable).Name("NonDateColumn");
// Other valid map statements
}
调用获取记录时,如果只有一个 map 语句失败或类型不匹配,则该行返回为空。
var record = reader.GetRecord<T>();
我正在寻找任何已成功映射的数据,以及要用于其余记录的默认值。
有办法实现吗?
我不认为有一种开箱即用的方法。但是,您可以创建自己的自定义转换器,如果它们无法解析数据,则 return 默认值。对于DateTime
,我从DateTimeConverter
中取出了GitHub project中的代码,如果它无法解析该字段,则将其修改为return DateTime.MinValue
。
public class Program
{
static void Main(string[] args)
{
using (var stream = new MemoryStream())
using (var writer = new StreamWriter(stream))
using (var reader = new StreamReader(stream))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
writer.WriteLine("Id,NonDateVariable,Bar");
writer.WriteLine("1,Not a date,bar value");
writer.WriteLine("2,2020/07/16,another value");
writer.Flush();
stream.Position = 0;
csv.Configuration.TypeConverterCache.AddConverter<DateTime>(new BadDateConverter());
csv.Configuration.RegisterClassMap<FooClassMap>();
var records = csv.GetRecords<Foo>();
}
}
}
public class Foo
{
public int Id { get; set; }
public DateTime DateVariable { get; set; }
public string Bar { get; set; }
}
public class FooClassMap : ClassMap<Foo>
{
public FooClassMap()
{
Map(m => m.Id);
Map(m => m.DateVariable).Name("NonDateVariable");
Map(m => m.Bar);
}
}
public class BadDateConverter : DefaultTypeConverter
{
public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
{
if (text == null)
{
return base.ConvertFromString(null, row, memberMapData);
}
var formatProvider = (IFormatProvider)memberMapData.TypeConverterOptions.CultureInfo.GetFormat(typeof(DateTimeFormatInfo)) ?? memberMapData.TypeConverterOptions.CultureInfo;
var dateTimeStyle = memberMapData.TypeConverterOptions.DateTimeStyle ?? DateTimeStyles.None;
if (memberMapData.TypeConverterOptions.Formats == null || memberMapData.TypeConverterOptions.Formats.Length == 0)
{
if (DateTime.TryParse(text, formatProvider, dateTimeStyle, out var d))
{
return d;
}
else
{
return DateTime.MinValue;
}
}
else
{
if (DateTime.TryParseExact(text, memberMapData.TypeConverterOptions.Formats, formatProvider, dateTimeStyle, out var d))
{
return d;
}
else
{
return DateTime.MinValue;
}
}
}
}
您还可以创建一个包装器转换器以在任何字段上使用。
public class NullIfErrorConverter : DefaultTypeConverter
{
public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
{
var type =
(memberMapData.Member as PropertyInfo)?.PropertyType ??
(memberMapData.Member as FieldInfo)?.FieldType ??
throw new InvalidOperationException($"Invalid member type. '{memberMapData.Member.MemberType}'");
var typeConverter = row.Configuration.TypeConverterCache.GetConverter(type);
memberMapData.TypeConverter = typeConverter;
try
{
return typeConverter.ConvertFromString(text, row, memberMapData);
}
catch
{
return null;
}
}
}
确保将它附加到映射中的成员而不是全局的,因为它使用的是全局类型缓存。
我正在使用 .NET 的 CSVHelper 库 https://github.com/JoshClose/CsvHelper
我有一张 class-map,如下所示:
public class MyMap : ClassMap<Product>
{
Map(c => c.DateVariable).Name("NonDateColumn");
// Other valid map statements
}
调用获取记录时,如果只有一个 map 语句失败或类型不匹配,则该行返回为空。
var record = reader.GetRecord<T>();
我正在寻找任何已成功映射的数据,以及要用于其余记录的默认值。
有办法实现吗?
我不认为有一种开箱即用的方法。但是,您可以创建自己的自定义转换器,如果它们无法解析数据,则 return 默认值。对于DateTime
,我从DateTimeConverter
中取出了GitHub project中的代码,如果它无法解析该字段,则将其修改为return DateTime.MinValue
。
public class Program
{
static void Main(string[] args)
{
using (var stream = new MemoryStream())
using (var writer = new StreamWriter(stream))
using (var reader = new StreamReader(stream))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
writer.WriteLine("Id,NonDateVariable,Bar");
writer.WriteLine("1,Not a date,bar value");
writer.WriteLine("2,2020/07/16,another value");
writer.Flush();
stream.Position = 0;
csv.Configuration.TypeConverterCache.AddConverter<DateTime>(new BadDateConverter());
csv.Configuration.RegisterClassMap<FooClassMap>();
var records = csv.GetRecords<Foo>();
}
}
}
public class Foo
{
public int Id { get; set; }
public DateTime DateVariable { get; set; }
public string Bar { get; set; }
}
public class FooClassMap : ClassMap<Foo>
{
public FooClassMap()
{
Map(m => m.Id);
Map(m => m.DateVariable).Name("NonDateVariable");
Map(m => m.Bar);
}
}
public class BadDateConverter : DefaultTypeConverter
{
public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
{
if (text == null)
{
return base.ConvertFromString(null, row, memberMapData);
}
var formatProvider = (IFormatProvider)memberMapData.TypeConverterOptions.CultureInfo.GetFormat(typeof(DateTimeFormatInfo)) ?? memberMapData.TypeConverterOptions.CultureInfo;
var dateTimeStyle = memberMapData.TypeConverterOptions.DateTimeStyle ?? DateTimeStyles.None;
if (memberMapData.TypeConverterOptions.Formats == null || memberMapData.TypeConverterOptions.Formats.Length == 0)
{
if (DateTime.TryParse(text, formatProvider, dateTimeStyle, out var d))
{
return d;
}
else
{
return DateTime.MinValue;
}
}
else
{
if (DateTime.TryParseExact(text, memberMapData.TypeConverterOptions.Formats, formatProvider, dateTimeStyle, out var d))
{
return d;
}
else
{
return DateTime.MinValue;
}
}
}
}
您还可以创建一个包装器转换器以在任何字段上使用。
public class NullIfErrorConverter : DefaultTypeConverter
{
public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
{
var type =
(memberMapData.Member as PropertyInfo)?.PropertyType ??
(memberMapData.Member as FieldInfo)?.FieldType ??
throw new InvalidOperationException($"Invalid member type. '{memberMapData.Member.MemberType}'");
var typeConverter = row.Configuration.TypeConverterCache.GetConverter(type);
memberMapData.TypeConverter = typeConverter;
try
{
return typeConverter.ConvertFromString(text, row, memberMapData);
}
catch
{
return null;
}
}
}
确保将它附加到映射中的成员而不是全局的,因为它使用的是全局类型缓存。