JoshClose / CsvHelper

Library to help reading and writing CSV files
http://joshclose.github.io/CsvHelper/
Other
4.65k stars 1.05k forks source link

Co-variant TypeConverterOptions #1690

Open jzabroski opened 3 years ago

jzabroski commented 3 years ago

Is your feature request related to a problem? Please describe. I frequently want to use a TypeConverterOption in a way similar to WPF ValueConverters. e.g., given the following file

Order,GTD Date
1,00/00/00

I want a class map like:

public class OrderLineClassMap : ClassMap<OrderLine>
{
  public OrderLineClassMap()
  {
    Map(m => m.LineNumber).ConvertUsing(row => row.Context.Row);
    Map(m => m.OrderNumber).Name("Order");
    Map(m => m.GTDDate).Name("GTD Date).TypeConverterOption.NullValues("00/00/00");
  }
}

However, when I try to parse this file, I get the error:

Read exception: An unexpected error occurred. : String was not recognized as a valid DateTime. : Row=[2] Field=[] Index=[2] Data=["1","00/00/00"] No values could be read. : Row=[2] Field=[] Index=[2] Data=["1","00/00/00"]

Describe the solution you'd like TypeConverterOption should be covariant to System.Object, not the MemberMap Type. This can be done by allowing interstitial manipulation on the string value, prior to doing the full blown conversion. Additionally, when Type Conversion fails, it should still populate Field=[] in the error message with Field=[GTD Date].

Describe alternatives you've considered

  1. Workaround alternative: Use ConvertUsing instead, which lets me intercept the value. However, that is not super elegant.
  2. Design alternative: Another option would be to make TypeConverterOption extensible similar to the fluent API we have in FluentMigrator.

Additional context I believe we've discussed this in the past, and that a more natural API would align closely with the System.ComponentModel.TypeConverter API supplied by the SDK libraries. I rather like how AutoMapper distinguishes the concepts of type converters, value resolvers and value converters:

Value converters are a cross between Type Converters and Value Resolvers. Type converters are globally scoped, so that any time you map from type Foo to type Bar in any mapping, the type converter will be used. Value converters are scoped to a single map, and receive the source and destination objects to resolve to a value to map to the destination member. Optionally value converters can receive the source member as well.

JoshClose commented 3 years ago

This works just fine for me if GTDate is nullable. If it's not nullable, I don't understand how it could work. If you convert and see that the value should be a null, how do you set a null to a value type?