Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.1k views
in Technique[技术] by (71.8m points)

c# - Custom conversions when writing CSV files using CsvHelper

I've been doing some CSV reading and writing lately, and ran across CsvHelper which is fantastic so far. I've ran into one small problem; I use a custom converter when reading the files, but when I write them back out, that format is lost. My CSV format looks like this:

67,1234-1,20150115,750,20150115,1340,549,549,406,0,FRG

The fields 20150115,750 map to a single DateTime field called Start (So, 01/15/2015 7:50 AM). My class map looks like this:

public sealed class DutyMap : CsvClassMap<Duty>
{
    static readonly CultureInfo enUS = new CultureInfo("en-US");

    public DutyMap()
    {
        Map(m => m.PersonId).Index(0);
        Map(m => m.DutyName).Index(1);
        Map(m => m.Start).ConvertUsing(row => ParseDate(row.GetField<String>(2), row.GetField<String>(3)));
        Map(m => m.End).ConvertUsing(row => ParseDate(row.GetField<String>(4), row.GetField<String>(5)));
        Map(m => m.DutyTime1).Index(6);
        Map(m => m.DutyTime2).Index(7);
        Map(m => m.FlightTime).Index(8);
        Map(m => m.CreditHours).Index(9);
        Map(m => m.DutyType).Index(10);
    }

    private static DateTime ParseDate(string date, string time)
    {
        DateTime ret;

        if (time.Length < 4)
            time = new String('0', 4 - time.Length) + time;

        if (!DateTime.TryParseExact(date + time, "yyyyMMddHHmm", enUS, DateTimeStyles.None, out ret))
            throw new FormatException(String.Format("Could not parse DateTime.  Date: {0} Time: {1}", date, time));

        return ret;
    }
}

This works great, and I can now call parse an entire file like so:

var csv = new CsvReader(sr);
csv.Configuration.HasHeaderRecord = false;
csv.Configuration.RegisterClassMap<DutyMap>();

Data = csv.GetRecords<Duty>().ToList();

However, when I write the file:

csv.WriteRecords(Data);

The file is written out like so:

67,7454-1,1/15/2015 7:50:00 AM,1/15/2015 1:40:00 PM,549,549,406,0,FPG

Looking through the documentation, I don't see a way to specify a conversation function upon writing, only reading. The only solution I've found so far is to manually write each record by hand:

var csv = new CsvWriter(sw);
foreach (var item in Data)
{
    csv.WriteField(item.PersonId);
    csv.WriteField(item.DutyName);
    csv.WriteField(item.Start.ToString("yyyyMMdd"));
    csv.WriteField(item.Start.ToString("Hmm"));
    csv.WriteField(item.End.ToString("yyyyMMdd"));
    csv.WriteField(item.End.ToString("Hmm"));
    csv.WriteField(item.DutyTime1);
    csv.WriteField(item.DutyTime2);
    csv.WriteField(item.FlightTime);
    csv.WriteField(item.CreditHours);
    csv.WriteField(item.DutyType);

    csv.NextRecord();
}

Is there a better way to do this? Thanks!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You can do it with a custom type converters (https://github.com/JoshClose/CsvHelper/wiki/Custom-TypeConverter). Just override ConvertToString method.

public class TestConverter : DefaultTypeConverter
{
    public override string ConvertToString(TypeConverterOptions options, object value)
    {
        return base.ConvertToString(options, value);
    }
}

and specify converter when mapping:

Map(m => m.PersonId).Index(0).TypeConverter<TestConverter>();

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...