diff --git a/src/FSharp.Data.Csv.Core/CsvInference.fs b/src/FSharp.Data.Csv.Core/CsvInference.fs index 14c4cb488..b15804e46 100644 --- a/src/FSharp.Data.Csv.Core/CsvInference.fs +++ b/src/FSharp.Data.Csv.Core/CsvInference.fs @@ -125,6 +125,7 @@ let internal inferCellType missingValues inferenceMode strictBooleans + preferFloats cultureInfo unit (value: string) @@ -140,7 +141,15 @@ let internal inferCellType InferedType.Null else let inferedType = - StructuralInference.getInferedTypeFromString unitsOfMeasureProvider inferenceMode cultureInfo value unit + if preferFloats then + StructuralInference.getInferedTypeFromStringPreferFloats + unitsOfMeasureProvider + inferenceMode + cultureInfo + value + unit + else + StructuralInference.getInferedTypeFromString unitsOfMeasureProvider inferenceMode cultureInfo value unit if strictBooleans then // With StrictBooleans=true, only "true"/"false" trigger bool inference. @@ -305,6 +314,7 @@ let internal inferType missingValues inferenceMode strictBooleans + preferFloats cultureInfo assumeMissingValues preferOptionals @@ -358,6 +368,7 @@ let internal inferType missingValues inferenceMode strictBooleans + preferFloats cultureInfo unit value @@ -456,6 +467,7 @@ let internal inferColumnTypes missingValues inferenceMode strictBooleans + preferFloats cultureInfo assumeMissingValues preferOptionals @@ -469,6 +481,7 @@ let internal inferColumnTypes missingValues inferenceMode strictBooleans + preferFloats cultureInfo assumeMissingValues preferOptionals @@ -497,7 +510,8 @@ type CsvFile with assumeMissingValues, preferOptionals, unitsOfMeasureProvider, - ?strictBooleans + ?strictBooleans, + ?preferFloats ) = let headerNamesAndUnits, schema = @@ -511,6 +525,7 @@ type CsvFile with missingValues inferenceMode (defaultArg strictBooleans false) + (defaultArg preferFloats false) cultureInfo assumeMissingValues preferOptionals diff --git a/src/FSharp.Data.DesignTime/Csv/CsvProvider.fs b/src/FSharp.Data.DesignTime/Csv/CsvProvider.fs index 85f700c4c..05c59fa9b 100644 --- a/src/FSharp.Data.DesignTime/Csv/CsvProvider.fs +++ b/src/FSharp.Data.DesignTime/Csv/CsvProvider.fs @@ -58,6 +58,7 @@ type public CsvProvider(cfg: TypeProviderConfig) as this = let preferDateOnly = args.[16] :?> bool let strictBooleans = args.[17] :?> bool let useOriginalNames = args.[18] :?> bool + let preferFloats = args.[19] :?> bool // This provider already has a schema mechanism, so let's disable inline schemas. let inferenceMode = InferenceMode'.ValuesOnly @@ -116,7 +117,8 @@ type public CsvProvider(cfg: TypeProviderConfig) as this = assumeMissingValues, preferOptionals, unitsOfMeasureProvider, - strictBooleans + strictBooleans, + preferFloats ) #if NET6_0_OR_GREATER if preferDateOnly && ProviderHelpers.runtimeSupportsNet6Types cfg.RuntimeAssembly then @@ -241,7 +243,8 @@ type public CsvProvider(cfg: TypeProviderConfig) as this = ProvidedStaticParameter("EmbeddedResource", typeof, parameterDefaultValue = "") ProvidedStaticParameter("PreferDateOnly", typeof, parameterDefaultValue = false) ProvidedStaticParameter("StrictBooleans", typeof, parameterDefaultValue = false) - ProvidedStaticParameter("UseOriginalNames", typeof, parameterDefaultValue = false) ] + ProvidedStaticParameter("UseOriginalNames", typeof, parameterDefaultValue = false) + ProvidedStaticParameter("PreferFloats", typeof, parameterDefaultValue = false) ] let helpText = """Typed representation of a CSV file. diff --git a/src/FSharp.Data.Html.Core/HtmlInference.fs b/src/FSharp.Data.Html.Core/HtmlInference.fs index 43e45375c..c56f8f46f 100644 --- a/src/FSharp.Data.Html.Core/HtmlInference.fs +++ b/src/FSharp.Data.Html.Core/HtmlInference.fs @@ -28,6 +28,7 @@ let internal inferColumns parameters (headerNamesAndUnits: _[]) rows = parameters.MissingValues parameters.InferenceMode false + false parameters.CultureInfo assumeMissingValues parameters.PreferOptionals diff --git a/src/FSharp.Data.Runtime.Utilities/StructuralInference.fs b/src/FSharp.Data.Runtime.Utilities/StructuralInference.fs index 71a6b4df8..ad7a68efa 100644 --- a/src/FSharp.Data.Runtime.Utilities/StructuralInference.fs +++ b/src/FSharp.Data.Runtime.Utilities/StructuralInference.fs @@ -525,6 +525,7 @@ let inferPrimitiveType (unitsOfMeasureProvider: IUnitsOfMeasureProvider) (inferenceMode: InferenceMode') (cultureInfo: CultureInfo) + (preferFloats: bool) (value: string) (desiredUnit: Type option) = @@ -573,7 +574,7 @@ let inferPrimitiveType makePrimitive typeof #endif | Parse TextConversions.AsDateTime date when not (isFakeDate date value) -> makePrimitive typeof - | Parse TextConversions.AsDecimal _ -> makePrimitive typeof + | Parse TextConversions.AsDecimal _ when not preferFloats -> makePrimitive typeof | Parse (TextConversions.AsFloat [||] false) _ -> makePrimitive typeof | Parse asGuid _ -> makePrimitive typeof | _ -> None @@ -622,7 +623,11 @@ let inferPrimitiveType /// Infers the type of a simple string value [] let getInferedTypeFromString unitsOfMeasureProvider inferenceMode cultureInfo value unit = - inferPrimitiveType unitsOfMeasureProvider inferenceMode cultureInfo value unit + inferPrimitiveType unitsOfMeasureProvider inferenceMode cultureInfo false value unit + +/// Infers the type of a simple string value, preferring float over decimal +let internal getInferedTypeFromStringPreferFloats unitsOfMeasureProvider inferenceMode cultureInfo value unit = + inferPrimitiveType unitsOfMeasureProvider inferenceMode cultureInfo true value unit #if NET6_0_OR_GREATER /// Replaces DateOnly → DateTime and TimeOnly → TimeSpan throughout an InferedType tree. diff --git a/tests/FSharp.Data.DesignTime.Tests/InferenceTests.fs b/tests/FSharp.Data.DesignTime.Tests/InferenceTests.fs index 414c82ec3..cdbdd4132 100644 --- a/tests/FSharp.Data.DesignTime.Tests/InferenceTests.fs +++ b/tests/FSharp.Data.DesignTime.Tests/InferenceTests.fs @@ -24,7 +24,7 @@ let internal unitsOfMeasureProvider = ProviderHelpers.unitsOfMeasureProvider let internal inferType (csv:CsvFile) inferRows missingValues cultureInfo schema assumeMissingValues preferOptionals = let headerNamesAndUnits, schema = parseHeaders csv.Headers csv.NumberOfColumns schema unitsOfMeasureProvider - inferType headerNamesAndUnits schema (csv.Rows |> Seq.map (fun x -> x.Columns)) inferRows missingValues inferenceMode false cultureInfo assumeMissingValues preferOptionals unitsOfMeasureProvider + inferType headerNamesAndUnits schema (csv.Rows |> Seq.map (fun x -> x.Columns)) inferRows missingValues inferenceMode false false cultureInfo assumeMissingValues preferOptionals unitsOfMeasureProvider let internal toRecord fields = InferedType.Record(None, fields, false) @@ -411,7 +411,7 @@ let ``Doesn't infer 12-002 as a date``() = [] let ``Doesn't infer ad3mar as a date``() = - StructuralInference.inferPrimitiveType unitsOfMeasureProvider inferenceMode CultureInfo.InvariantCulture "ad3mar" None + StructuralInference.inferPrimitiveType unitsOfMeasureProvider inferenceMode CultureInfo.InvariantCulture false "ad3mar" None |> should equal (InferedType.Primitive(typeof, None, false, false)) [] diff --git a/tests/FSharp.Data.DesignTime.Tests/TypeProviderInstantiation.fs b/tests/FSharp.Data.DesignTime.Tests/TypeProviderInstantiation.fs index c42b50876..bd060f25b 100644 --- a/tests/FSharp.Data.DesignTime.Tests/TypeProviderInstantiation.fs +++ b/tests/FSharp.Data.DesignTime.Tests/TypeProviderInstantiation.fs @@ -28,7 +28,8 @@ type internal CsvProviderArgs = EmbeddedResource : string PreferDateOnly : bool StrictBooleans : bool - UseOriginalNames : bool } + UseOriginalNames : bool + PreferFloats : bool } type internal XmlProviderArgs = { Sample : string @@ -106,7 +107,8 @@ type internal TypeProviderInstantiation = box x.EmbeddedResource box x.PreferDateOnly box x.StrictBooleans - box x.UseOriginalNames |] + box x.UseOriginalNames + box x.PreferFloats |] | Xml x -> (fun cfg -> new XmlProvider(cfg) :> TypeProviderForNamespaces), [| box x.Sample @@ -246,7 +248,8 @@ type internal TypeProviderInstantiation = EmbeddedResource = "" PreferDateOnly = false StrictBooleans = false - UseOriginalNames = false } + UseOriginalNames = false + PreferFloats = false } | "Xml" -> Xml { Sample = args.[1] SampleIsList = args.[2] |> bool.Parse diff --git a/tests/FSharp.Data.Tests/CsvProvider.fs b/tests/FSharp.Data.Tests/CsvProvider.fs index 7d24802a3..4187bc3d7 100644 --- a/tests/FSharp.Data.Tests/CsvProvider.fs +++ b/tests/FSharp.Data.Tests/CsvProvider.fs @@ -766,3 +766,21 @@ let ``CsvProvider Row With* methods do not mutate the original row`` () = let row = csv.Rows |> Seq.head let _ = row.WithName("Charlie") row.Name |> should equal "Alice" + +// Tests for PreferFloats static parameter (issue #838) +let [] csvWithDecimals = "Name,Price,Rate\nAlice,9.99,1.5\nBob,12.50,2.7" + +type CsvPreferFloats = CsvProvider +type CsvDefaultDecimal = CsvProvider + +[] +let ``CsvProvider PreferFloats=true infers float instead of decimal`` () = + let row = CsvPreferFloats.GetSample().Rows |> Seq.head + row.Price |> should equal 9.99 + row.Rate |> should equal 1.5 + +[] +let ``CsvProvider PreferFloats=false (default) infers decimal for decimal values`` () = + let row = CsvDefaultDecimal.GetSample().Rows |> Seq.head + row.Price |> should equal 9.99m + row.Rate |> should equal 1.5m