自定义 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 本身。