自定义 DateTime 转换器返回 null?
Custom DateTime coverter is returning null?
我有一个 DateTime 类型的模型 属性。从这个 属性,我想在写入 CSV 文件和通过 CsvHelper 从 CSV 文件读取时将秒和微秒包含到字符串中。
这就是我通过扩展 DefaultTypeConverter
得出解决方案的方法
public class CsvDateTimeConverter : DefaultTypeConverter
{
private const string DateStringFormat = "MM/dd/yyyy HH:mm:ss.fff";
public override object ConvertFromString(TypeConverterOptions options, string text)
{
if (string.IsNullOrEmpty(text)) return null;
return Convert.ToDateTime(text);
}
public override string ConvertToString(TypeConverterOptions options, object value)
{
if (value == null) return "";
var dateTime = (DateTime) value;
return dateTime.ToString(DateStringFormat);
}
}
模型看起来像这样
public class MyModel
{
// ..... removed other properties
public DateTime MyDateTime {get; set};
}
CsvClassMapper 看起来像这样
public sealed class CsvMap : CsvClassMap<MyModel>
{
public CsvMap()
{
Map(m => m.MyDateTime).TypeConverter<CsvDateTimeConverter>();
}
}
我将 class 地图注册到 reader 和作家。但问题是:
我可以使用转换器成功写入 CSV 文件,但是当我尝试从 CSV 文件中读取时,它可以读取其他属性,但 returns DateTime 属性 为 null。
所以..我错过了什么?
已编辑:
如果有帮助:
这是从 csv 文件中读取的代码:
using (TextReader reader = new StreamReader(filePath))
{
var csv = new CsvReader(reader);
csv.Configuration.RegisterClassMap<CsvMap>();
csv.Configuration.Encoding = Encoding.UTF8;
return csv.GetRecords<MyModel>().ToList();
}
public override object ConvertFromString(TypeConverterOptions options, string text)
{
if (string.IsNullOrEmpty(text)) return null;
return Convert.ToDateTime(text);
}
您可能在这里遗漏了特殊格式。我是 DateTime.TryConvert(...) 的粉丝 - 我自己不喜欢 Convert.XXXX。是否有理由在特定于类型的转换方法上使用该方法?
这个方法
Convert.ToDateTime(text);
有一个采用您必须在此处指定的格式的重载。试试看。
编辑:
在您的 ConvertFromString 方法中添加:
public override object ConvertFromString(TypeConverterOptions options, string text)
{
if (string.IsNullOrEmpty(text)) return null;
DateTimeFormatInfo info = new DateTimeFormatInfo() { FullDateTimePattern = DateStringFormat };
return Convert.ToDateTime(text, info);
}
您不需要指定自定义类型转换器。这是美国风格的 DateTime 格式,添加了毫秒。您只需指定正确的区域性,并可能指定要使用的格式。
一种选择是将 Configuration.CultureInfo
属性 设置为美国文化:
reader.Configuration.CultureInfo = CultureInfo.GetCultureInfo("en-US");
以下代码将生成并读取美国日期 ,无需 毫秒:
using (var file = new StreamWriter("test.csv"))
{
var writer = new CsvWriter(file);
writer.Configuration.CultureInfo = CultureInfo.GetCultureInfo("en-US");
writer.WriteRecords(items);
}
using (var file = new StreamReader("test.csv"))
{
var reader= new CsvReader(file);
reader.Configuration.CultureInfo = CultureInfo.GetCultureInfo("en-US");
var models=reader.GetRecords<MyModel>().ToArray();
Console.WriteLine(models[0]);
}
您可以通过 class 映射中的 TypeConverterOptions
方法为字段指定不同的格式:
public sealed class CsvMap : CsvHelper.Configuration.CsvClassMap<MyModel>
{
public CsvMap()
{
Map(m => m.Date).TypeConverterOption("MM/dd/yyyy HH:mm:ss.fff");
Map(m => m.ID);
}
}
以下代码仅添加 class 地图,将生成以毫秒为单位的美国日期:
using (var file = new StreamWriter("test.csv"))
{
var writer = new CsvWriter(file);
writer.Configuration.CultureInfo = CultureInfo.GetCultureInfo("en-US");
writer.Configuration.RegisterClassMap<CsvMap>();
writer.WriteRecords(items);
}
using (var file = new StreamReader("test.csv"))
{
var reader= new CsvReader(file);
reader.Configuration.CultureInfo = CultureInfo.GetCultureInfo("en-US");
reader.Configuration.RegisterClassMap<CsvMap>();
var models=reader.GetRecords<MyModel>().ToArray();
Console.WriteLine(models[0]);
}
Date,ID
10/12/2017 11:56:11.016,1
10/11/2017 11:56:11.021,2
如果您不想为整个文件设置文化,您也可以将 CultureInfo 指定为 TypeConverterOption:
public CsvMap()
{
Map(m => m.Date).TypeConverterOption("MM/dd/yyyy HH:mm:ss.fff")
.TypeConverterOption(CultureInfo.GetCultureInfo("en-US"));
Map(m => m.ID);
}
注意
CsvHelper 最近(本周)发布了一个重大更新 (3.0)。上周还没有!当前版本为 3.2.0.
在此版本中,CsvClassMap
变为 CsvMap
并且 TypeConverterOptions 方法变为具有 return MapMember :
方法的对象
public CsvMap()
{
string format="MM/dd/yyyy HH:mm:ss.fff";
var enUS=CultureInfo.GetCultureInfo("en-US");
Map(m => m.Date).TypeConverterOption.Format(format)
.TypeConverterOption.CultureInfo(enUS);
Map(m => m.ID);
}
仍然没有 Type Converters much less TypeConverterOptions. I found the method from this Github issue 的文档,应该作为文档。
另一种选择是检查 the source code 本身。
我有一个 DateTime 类型的模型 属性。从这个 属性,我想在写入 CSV 文件和通过 CsvHelper 从 CSV 文件读取时将秒和微秒包含到字符串中。
这就是我通过扩展 DefaultTypeConverter
得出解决方案的方法public class CsvDateTimeConverter : DefaultTypeConverter
{
private const string DateStringFormat = "MM/dd/yyyy HH:mm:ss.fff";
public override object ConvertFromString(TypeConverterOptions options, string text)
{
if (string.IsNullOrEmpty(text)) return null;
return Convert.ToDateTime(text);
}
public override string ConvertToString(TypeConverterOptions options, object value)
{
if (value == null) return "";
var dateTime = (DateTime) value;
return dateTime.ToString(DateStringFormat);
}
}
模型看起来像这样
public class MyModel
{
// ..... removed other properties
public DateTime MyDateTime {get; set};
}
CsvClassMapper 看起来像这样
public sealed class CsvMap : CsvClassMap<MyModel>
{
public CsvMap()
{
Map(m => m.MyDateTime).TypeConverter<CsvDateTimeConverter>();
}
}
我将 class 地图注册到 reader 和作家。但问题是:
我可以使用转换器成功写入 CSV 文件,但是当我尝试从 CSV 文件中读取时,它可以读取其他属性,但 returns DateTime 属性 为 null。
所以..我错过了什么?
已编辑:
如果有帮助:
这是从 csv 文件中读取的代码:
using (TextReader reader = new StreamReader(filePath))
{
var csv = new CsvReader(reader);
csv.Configuration.RegisterClassMap<CsvMap>();
csv.Configuration.Encoding = Encoding.UTF8;
return csv.GetRecords<MyModel>().ToList();
}
public override object ConvertFromString(TypeConverterOptions options, string text)
{
if (string.IsNullOrEmpty(text)) return null;
return Convert.ToDateTime(text);
}
您可能在这里遗漏了特殊格式。我是 DateTime.TryConvert(...) 的粉丝 - 我自己不喜欢 Convert.XXXX。是否有理由在特定于类型的转换方法上使用该方法?
这个方法
Convert.ToDateTime(text);
有一个采用您必须在此处指定的格式的重载。试试看。
编辑:
在您的 ConvertFromString 方法中添加:
public override object ConvertFromString(TypeConverterOptions options, string text)
{
if (string.IsNullOrEmpty(text)) return null;
DateTimeFormatInfo info = new DateTimeFormatInfo() { FullDateTimePattern = DateStringFormat };
return Convert.ToDateTime(text, info);
}
您不需要指定自定义类型转换器。这是美国风格的 DateTime 格式,添加了毫秒。您只需指定正确的区域性,并可能指定要使用的格式。
一种选择是将 Configuration.CultureInfo
属性 设置为美国文化:
reader.Configuration.CultureInfo = CultureInfo.GetCultureInfo("en-US");
以下代码将生成并读取美国日期 ,无需 毫秒:
using (var file = new StreamWriter("test.csv"))
{
var writer = new CsvWriter(file);
writer.Configuration.CultureInfo = CultureInfo.GetCultureInfo("en-US");
writer.WriteRecords(items);
}
using (var file = new StreamReader("test.csv"))
{
var reader= new CsvReader(file);
reader.Configuration.CultureInfo = CultureInfo.GetCultureInfo("en-US");
var models=reader.GetRecords<MyModel>().ToArray();
Console.WriteLine(models[0]);
}
您可以通过 class 映射中的 TypeConverterOptions
方法为字段指定不同的格式:
public sealed class CsvMap : CsvHelper.Configuration.CsvClassMap<MyModel>
{
public CsvMap()
{
Map(m => m.Date).TypeConverterOption("MM/dd/yyyy HH:mm:ss.fff");
Map(m => m.ID);
}
}
以下代码仅添加 class 地图,将生成以毫秒为单位的美国日期:
using (var file = new StreamWriter("test.csv"))
{
var writer = new CsvWriter(file);
writer.Configuration.CultureInfo = CultureInfo.GetCultureInfo("en-US");
writer.Configuration.RegisterClassMap<CsvMap>();
writer.WriteRecords(items);
}
using (var file = new StreamReader("test.csv"))
{
var reader= new CsvReader(file);
reader.Configuration.CultureInfo = CultureInfo.GetCultureInfo("en-US");
reader.Configuration.RegisterClassMap<CsvMap>();
var models=reader.GetRecords<MyModel>().ToArray();
Console.WriteLine(models[0]);
}
Date,ID
10/12/2017 11:56:11.016,1
10/11/2017 11:56:11.021,2
如果您不想为整个文件设置文化,您也可以将 CultureInfo 指定为 TypeConverterOption:
public CsvMap()
{
Map(m => m.Date).TypeConverterOption("MM/dd/yyyy HH:mm:ss.fff")
.TypeConverterOption(CultureInfo.GetCultureInfo("en-US"));
Map(m => m.ID);
}
注意
CsvHelper 最近(本周)发布了一个重大更新 (3.0)。上周还没有!当前版本为 3.2.0.
在此版本中,CsvClassMap
变为 CsvMap
并且 TypeConverterOptions 方法变为具有 return MapMember :
public CsvMap()
{
string format="MM/dd/yyyy HH:mm:ss.fff";
var enUS=CultureInfo.GetCultureInfo("en-US");
Map(m => m.Date).TypeConverterOption.Format(format)
.TypeConverterOption.CultureInfo(enUS);
Map(m => m.ID);
}
仍然没有 Type Converters much less TypeConverterOptions. I found the method from this Github issue 的文档,应该作为文档。
另一种选择是检查 the source code 本身。