mperdeck / LINQtoCSV

Popular, easy to use library to read and write CSV files.
197 stars 112 forks source link

Parsing nullable decimal fails #45

Open chrispday opened 9 years ago

chrispday commented 9 years ago

Parsing a field declared as decimal? fails with a FormatException with the value "2,000,000".

This is caused because when the TypeFieldInfo class (inside FieldMapper class) sets up the converters it uses the fields PropertyType or FieldType to determine if that type has a Parse or ParseExact method (FieldMapper.cs lines 96-114). If it doesn't it falls back to a type converter. Unfortunately the type converter hard codes NumberStyles.Float (DecimalConverter.FromString(string, NumberFormatInfo) which won't parse decimal strings with a comma as a thousands seperator.

To fix the field type should be checked for nullable, and if it is to use the underlying type to test for a Parse or ParseExact method.

The only work around as it currently stands is to change the type of the field from decimal? to decimal and use the CanBeNull=true attribute.

aifdsc commented 8 years ago

@chrispday I haven't done an official pull request yet, but I ran into the same problem today. I put together a quick bit of code to fix it; you're welcome to use it as-is or improve it if needed. I have not done full testing on it, but it is covering my needs so far. Hope this can help!

In FieldMapper.cs, I went to the AnalyzeTypeField method and entered the following block, replacing the section of code between the //parseNumberMethod comment block and the //Process the attributes comment block.

        var type = tfi.fieldType;
        var isNullable = type.IsGenericType && type.GetGenericTypeDefinition( ) == typeof( Nullable<> );
        if ( isNullable )
            type = type.GetGenericArguments( )[0];

        tfi.parseNumberMethod =
            type.GetMethod( "Parse",
                new Type[] { typeof( String ), typeof( NumberStyles ), typeof( IFormatProvider ) } );

        if ( tfi.parseNumberMethod == null )
        {
            if ( m_fileDescription.UseOutputFormatForParsingCsvValue )
            {
                tfi.parseExactMethod = type.GetMethod( "ParseExact",
                    new Type[] { typeof( string ), typeof( string ), typeof( IFormatProvider ) } );
            }

            tfi.typeConverter = null;
            if ( tfi.parseExactMethod == null )
            {
                tfi.typeConverter =
                    TypeDescriptor.GetConverter( type );
            }
        }