【问题标题】:Custom DateTime coverter is returning null?自定义日期时间转换器返回 null?
【发布时间】:2017-10-12 08:06:48
【问题描述】:

我有一个具有 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>();
    }
}

我将类映射注册到阅读器和作者。但问题是:

我可以使用转换器成功写入 CSV 文件,但是当我尝试从 CSV 文件中读取时,它可以读取其他属性,但 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();
 }

【问题讨论】:

  • 用 DateTime.ParseExact() 替换 Convert.ToDateTime(text) 并使用格式:"MM/dd/yyyy HH:mm:ss.fff"
  • 什么自定义日期时间转换器?这不会转换任何东西,它只是对Convert.ToDateTime(text) 的简单调用。这将使用当前语言环境的格式
  • CsvDateTimeConverter 中的代码似乎工作正常(如果我直接调用它)。也许是转换器注册的问题?见C# Fiddle
  • 这是美国格式。您不需要为此使用自定义转换器。您是否尝试过通过Configuration.CultureInfo 设置文化?
  • @ash 你不需要使用自定义类型转换器。您可以通过 TypeConverterOption 指定格式、文化等。 +1,因为如果不是这个问题,我也不会找到它

标签: c# datetime csvhelper


【解决方案1】:

您不需要指定自定义类型转换器。这是美国风格的 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]);                
        }

您可以通过类映射中的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);
    }
}

下面的代码,只是添加了类映射,将生成以毫秒为单位的美国日期:

        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 方法变为一个对象,其方法返回 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 仍然没有文档,更不用说 TypeConverterOptions。我从this Github issue 找到了应该作为文档的方法。

另一个选项是检查the source code 本身。

【讨论】:

    【解决方案2】:
    public override object ConvertFromString(TypeConverterOptions options, string text)
    {
        if (string.IsNullOrEmpty(text)) return null;
    
        return Convert.ToDateTime(text);
    }
    

    您可能在这里遗漏了您的特殊格式。我是 DateTime.TryConvert(...) 的粉丝 - 我自己不喜欢 Convert.XXXX。是否有理由在特定于类型的转换方法上使用该方法?

    【讨论】:

    • Convert. 方法接受格式字符串。这是由每种类型的 ParseExact 方法提供的
    【解决方案3】:

    这个方法

    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);
    }
    

    【讨论】:

    • 仔细看图片。那是CultureInfo,而不是格式字符串
    • 你不会在 DateTimeFormatInfo 中定义 DatePattern 并且不会被给定的方法使用吗?
    • @Rob:您可能会这么认为,但问题中的private const string DateStringFormat = "MM/dd/yyyy HH:mm:ss.fff"; 显然不同意您的观点。总的来说,当您想要的只是日期格式时,我不确定您为什么要创建或查找CultureInfo。清楚地显示日期格式(如本例所示)意味着毫无疑问应该采用什么格式 - 无需查找文化以找出他们使用的格式等。
    • 我并不是说你必须查找一种文化。只需创建一个新的 DateTimeFormatInfo 并将属性 FullDateTimePattern 指定为 OP 指定的格式。而已!您可以像这样定义一个日期 - 12WhatsUp10WhatsUp2017 并创建一个 DateTimeFormatInfo ,它将 FullDateTimePattern 定义为“ddMMyyy”,在 DateSeparator 中您可以放入 WhatsUp 并且您的 Convert.ToDateTime 方法将转换它。试试看!反正他只对 FullDateTimePattern 感兴趣。
    • @Rob 人们使用DateTime.ParseExact而不是Convert.ToDateTime是有原因的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-15
    • 1970-01-01
    • 2014-08-23
    • 1970-01-01
    相关资源
    最近更新 更多