Skip to content
Merged
16 changes: 13 additions & 3 deletions src/FSharp.Data.DesignTime/Csv/CsvGenerator.fs
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,19 @@ module internal CsvTypeBuilder =
ProvidedParameter: ProvidedParameter
}

let generateTypes asm ns typeName (missingValuesStr, cultureStr) inferredFields =
let generateTypes asm ns typeName (missingValuesStr, cultureStr) useOriginalNames inferredFields =

let fields =
inferredFields
|> List.mapi (fun index field ->
let typ, typWithoutMeasure, conv, convBack =
ConversionsGenerator.convertStringValue missingValuesStr cultureStr field

let propertyName = NameUtils.capitalizeFirstLetter field.Name
let propertyName =
if useOriginalNames then
field.Name
else
NameUtils.capitalizeFirstLetter field.Name

let prop =
ProvidedProperty(
Expand All @@ -58,11 +62,17 @@ module internal CsvTypeBuilder =
| _ -> Expr.TupleGet(rowVarExpr, index)
)

let paramName =
if useOriginalNames then
field.Name
else
NameUtils.niceCamelName propertyName

{ TypeForTuple = typWithoutMeasure
ProvidedProperty = prop
Convert = convert
ConvertBack = convertBack
ProvidedParameter = ProvidedParameter(NameUtils.niceCamelName propertyName, typ) })
ProvidedParameter = ProvidedParameter(paramName, typ) })

// The erased row type will be a tuple of all the field types (without the units of measure). If there is a single column then it is just the column type.
let rowErasedType =
Expand Down
9 changes: 6 additions & 3 deletions src/FSharp.Data.DesignTime/Csv/CsvProvider.fs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ type public CsvProvider(cfg: TypeProviderConfig) as this =
let resource = args.[15] :?> string
let preferDateOnly = args.[16] :?> bool
let strictBooleans = args.[17] :?> bool
let useOriginalNames = args.[18] :?> bool

// This provider already has a schema mechanism, so let's disable inline schemas.
let inferenceMode = InferenceMode'.ValuesOnly
Expand Down Expand Up @@ -130,7 +131,7 @@ type public CsvProvider(cfg: TypeProviderConfig) as this =

let csvType, csvErasedType, rowType, stringArrayToRow, rowToStringArray =
inferredFields
|> CsvTypeBuilder.generateTypes asm ns typeName (missingValuesStr, cultureStr)
|> CsvTypeBuilder.generateTypes asm ns typeName (missingValuesStr, cultureStr) useOriginalNames

let stringArrayToRowVar = Var("stringArrayToRow", stringArrayToRow.Type)
let rowToStringArrayVar = Var("rowToStringArray", rowToStringArray.Type)
Expand Down Expand Up @@ -239,7 +240,8 @@ type public CsvProvider(cfg: TypeProviderConfig) as this =
ProvidedStaticParameter("ResolutionFolder", typeof<string>, parameterDefaultValue = "")
ProvidedStaticParameter("EmbeddedResource", typeof<string>, parameterDefaultValue = "")
ProvidedStaticParameter("PreferDateOnly", typeof<bool>, parameterDefaultValue = false)
ProvidedStaticParameter("StrictBooleans", typeof<bool>, parameterDefaultValue = false) ]
ProvidedStaticParameter("StrictBooleans", typeof<bool>, parameterDefaultValue = false)
ProvidedStaticParameter("UseOriginalNames", typeof<bool>, parameterDefaultValue = false) ]

let helpText =
"""<summary>Typed representation of a CSV file.</summary>
Expand All @@ -264,7 +266,8 @@ type public CsvProvider(cfg: TypeProviderConfig) as this =
<param name='EmbeddedResource'>When specified, the type provider first attempts to load the sample from the specified resource
(e.g. 'MyCompany.MyAssembly, resource_name.csv'). This is useful when exposing types generated by the type provider.</param>
<param name='PreferDateOnly'>When true on .NET 6+, date-only strings are inferred as DateOnly and time-only strings as TimeOnly. Defaults to false for backward compatibility.</param>
<param name='StrictBooleans'>When true, only <c>true</c> and <c>false</c> (case-insensitive) are inferred as boolean. Values such as <c>0</c>, <c>1</c>, <c>yes</c>, and <c>no</c> are treated as integers or strings respectively. Defaults to false.</param>"""
<param name='StrictBooleans'>When true, only <c>true</c> and <c>false</c> (case-insensitive) are inferred as boolean. Values such as <c>0</c>, <c>1</c>, <c>yes</c>, and <c>no</c> are treated as integers or strings respectively. Defaults to false.</param>
<param name='UseOriginalNames'>When true, CSV column header names are used as-is for generated property names instead of being normalized (e.g. capitalizing the first letter). Defaults to false.</param>"""

do csvProvTy.AddXmlDoc helpText
do csvProvTy.DefineStaticParameters(parameters, buildTypes)
Expand Down
46 changes: 37 additions & 9 deletions src/FSharp.Data.DesignTime/Json/JsonGenerator.fs
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,26 @@ type internal JsonGenerationContext =
PreferDictionaries: bool
GenerateConstructors: bool
InferenceMode: InferenceMode'
UnitsOfMeasureProvider: IUnitsOfMeasureProvider }
UnitsOfMeasureProvider: IUnitsOfMeasureProvider
UseOriginalNames: bool }

static member Create
(cultureStr, tpType, unitsOfMeasureProvider, inferenceMode, ?uniqueNiceName, ?typeCache, ?preferDictionaries)
=
(
cultureStr,
tpType,
unitsOfMeasureProvider,
inferenceMode,
?uniqueNiceName,
?typeCache,
?preferDictionaries,
?useOriginalNames
) =
let useOriginalNames = defaultArg useOriginalNames false

let uniqueNiceName =
defaultArg uniqueNiceName (NameUtils.uniqueGenerator NameUtils.nicePascalName)
defaultArg
uniqueNiceName
(NameUtils.uniqueGenerator (if useOriginalNames then id else NameUtils.nicePascalName))

let typeCache = defaultArg typeCache (Dictionary())
let preferDictionaries = defaultArg preferDictionaries false
Expand All @@ -49,7 +62,8 @@ type internal JsonGenerationContext =
preferDictionaries,
true,
inferenceMode,
unitsOfMeasureProvider
unitsOfMeasureProvider,
useOriginalNames
)

static member Create
Expand All @@ -61,7 +75,8 @@ type internal JsonGenerationContext =
preferDictionaries,
generateConstructors,
inferenceMode,
unitsOfMeasureProvider
unitsOfMeasureProvider,
useOriginalNames
) =
{ CultureStr = cultureStr
TypeProviderType = tpType
Expand All @@ -73,7 +88,8 @@ type internal JsonGenerationContext =
PreferDictionaries = preferDictionaries
GenerateConstructors = generateConstructors
InferenceMode = inferenceMode
UnitsOfMeasureProvider = unitsOfMeasureProvider }
UnitsOfMeasureProvider = unitsOfMeasureProvider
UseOriginalNames = useOriginalNames }

member x.MakeOptionType(typ: Type) =
typedefof<option<_>>.MakeGenericType typ
Expand Down Expand Up @@ -376,7 +392,13 @@ module JsonTypeBuilder =
ctx.TypeProviderType.AddMember(objectTy)

// to nameclash property names
let makeUnique = NameUtils.uniqueGenerator NameUtils.nicePascalName
let nicePropertyName =
if ctx.UseOriginalNames then
id
else
NameUtils.nicePascalName

let makeUnique = NameUtils.uniqueGenerator nicePropertyName
makeUnique "JsonValue" |> ignore

let inferedKeyValueType =
Expand Down Expand Up @@ -600,7 +622,13 @@ module JsonTypeBuilder =

prop.Name,
[ ProvidedProperty(name, convertedType, getterCode = getter) ],
ProvidedParameter(NameUtils.niceCamelName name, replaceJDocWithJValue ctx convertedType) ]
ProvidedParameter(
(if ctx.UseOriginalNames then
name
else
NameUtils.niceCamelName name),
replaceJDocWithJValue ctx convertedType
) ]

let names, properties, parameters = List.unzip3 members
let properties = properties |> List.concat
Expand Down
10 changes: 7 additions & 3 deletions src/FSharp.Data.DesignTime/Json/JsonProvider.fs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type public JsonProvider(cfg: TypeProviderConfig) as this =
let inferenceMode = args.[9] :?> InferenceMode
let schema = args.[10] :?> string
let preferDateOnly = args.[11] :?> bool
let useOriginalNames = args.[12] :?> bool

let inferenceMode =
InferenceMode'.FromPublicApi(inferenceMode, inferTypesFromValues)
Expand Down Expand Up @@ -117,7 +118,8 @@ type public JsonProvider(cfg: TypeProviderConfig) as this =
tpType,
unitsOfMeasureProvider,
inferenceMode,
?preferDictionaries = Some preferDictionaries
?preferDictionaries = Some preferDictionaries,
?useOriginalNames = Some useOriginalNames
)

let result = JsonTypeBuilder.generateJsonType ctx false false rootName inferedType
Expand Down Expand Up @@ -164,7 +166,8 @@ type public JsonProvider(cfg: TypeProviderConfig) as this =
parameterDefaultValue = InferenceMode.BackwardCompatible
)
ProvidedStaticParameter("Schema", typeof<string>, parameterDefaultValue = "")
ProvidedStaticParameter("PreferDateOnly", typeof<bool>, parameterDefaultValue = false) ]
ProvidedStaticParameter("PreferDateOnly", typeof<bool>, parameterDefaultValue = false)
ProvidedStaticParameter("UseOriginalNames", typeof<bool>, parameterDefaultValue = false) ]

let helpText =
"""<summary>Typed representation of a JSON document.</summary>
Expand All @@ -188,7 +191,8 @@ type public JsonProvider(cfg: TypeProviderConfig) as this =
| ValuesAndInlineSchemasOverrides -> Same as ValuesAndInlineSchemasHints, but value inferred types are ignored when an inline schema is present.
</param>
<param name='Schema'>Location of a JSON Schema file or a string containing a JSON Schema document. When specified, Sample and SampleIsList must not be used.</param>
<param name='PreferDateOnly'>When true on .NET 6+, date-only strings (e.g. "2023-01-15") are inferred as DateOnly and time-only strings as TimeOnly. Defaults to false for backward compatibility.</param>"""
<param name='PreferDateOnly'>When true on .NET 6+, date-only strings (e.g. "2023-01-15") are inferred as DateOnly and time-only strings as TimeOnly. Defaults to false for backward compatibility.</param>
<param name='UseOriginalNames'>When true, JSON property names are used as-is for generated property names instead of being normalized to PascalCase. Defaults to false.</param>"""

do jsonProvTy.AddXmlDoc helpText
do jsonProvTy.DefineStaticParameters(parameters, buildTypes)
Expand Down
43 changes: 30 additions & 13 deletions src/FSharp.Data.DesignTime/Xml/XmlGenerator.fs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ type internal XmlGenerationContext =
UniqueNiceName: string -> string
UnifyGlobally: bool
XmlTypeCache: Dictionary<InferedType, XmlGenerationResult>
JsonTypeCache: Dictionary<InferedType, ProvidedTypeDefinition> }
JsonTypeCache: Dictionary<InferedType, ProvidedTypeDefinition>
UseOriginalNames: bool }

static member Create(unitsOfMeasureProvider, inferenceMode, cultureStr, tpType, unifyGlobally) =
let uniqueNiceName = NameUtils.uniqueGenerator NameUtils.nicePascalName
static member Create(unitsOfMeasureProvider, inferenceMode, cultureStr, tpType, unifyGlobally, useOriginalNames) =
let niceName = if useOriginalNames then id else NameUtils.nicePascalName
let uniqueNiceName = NameUtils.uniqueGenerator niceName
uniqueNiceName "XElement" |> ignore

{ CultureStr = cultureStr
Expand All @@ -42,7 +44,8 @@ type internal XmlGenerationContext =
UniqueNiceName = uniqueNiceName
UnifyGlobally = unifyGlobally
XmlTypeCache = Dictionary()
JsonTypeCache = Dictionary() }
JsonTypeCache = Dictionary()
UseOriginalNames = useOriginalNames }

member x.ConvertValue prop =
let typ, _, conv, _ = ConversionsGenerator.convertStringValue "" x.CultureStr prop
Expand Down Expand Up @@ -220,7 +223,14 @@ module internal XmlTypeBuilder =
ctx.ProvidedType.AddMember objectTy

// to nameclash property names
let makeUnique = NameUtils.uniqueGenerator NameUtils.nicePascalName
let nicePropertyName =
if ctx.UseOriginalNames then
id
else
NameUtils.nicePascalName

let niceCamelName = if ctx.UseOriginalNames then id else NameUtils.niceCamelName
let makeUnique = NameUtils.uniqueGenerator nicePropertyName
makeUnique "XElement" |> ignore

// For each case, add property of optional type
Expand All @@ -245,7 +255,7 @@ module internal XmlTypeBuilder =
""
else
nameWithNS),
ProvidedParameter(NameUtils.niceCamelName name, result.ConvertedType)) ]
ProvidedParameter(niceCamelName name, result.ConvertedType)) ]

let properties, parameters = List.unzip members
objectTy.AddMembers properties
Expand Down Expand Up @@ -304,7 +314,14 @@ module internal XmlTypeBuilder =
let attrs, content = props |> List.partition (fun prop -> prop.Name <> "")

// to nameclash property names
let makeUnique = NameUtils.uniqueGenerator NameUtils.nicePascalName
let nicePropertyName =
if ctx.UseOriginalNames then
id
else
NameUtils.nicePascalName

let niceCamelName = if ctx.UseOriginalNames then id else NameUtils.niceCamelName
let makeUnique = NameUtils.uniqueGenerator nicePropertyName
makeUnique "XElement" |> ignore

// Generate properties for all XML attributes
Expand All @@ -322,7 +339,7 @@ module internal XmlTypeBuilder =
getterCode =
fun (Singleton xml) -> conv <@ XmlRuntime.TryGetAttribute(%%xml, nameWithNS) @>
),
ProvidedParameter(NameUtils.niceCamelName name, typ)
ProvidedParameter(niceCamelName name, typ)

let createPrimitiveMember typ unit (optional: bool) =
let typ, conv =
Expand Down Expand Up @@ -417,7 +434,7 @@ module internal XmlTypeBuilder =
let name = makeUnique name

ProvidedProperty(name, typ, getterCode = (fun (Singleton xml) -> conv xml)),
ProvidedParameter(NameUtils.niceCamelName name, nonOptionalType) ]
ProvidedParameter(niceCamelName name, nonOptionalType) ]

// For every possible child element, generate a getter property
let childResults =
Expand Down Expand Up @@ -469,7 +486,7 @@ module internal XmlTypeBuilder =
fun (Singleton xml) ->
result.Converter <@@ XmlRuntime.GetChild(%%xml, nameWithNS) @@>
),
ProvidedParameter(NameUtils.niceCamelName name, result.ConvertedType)
ProvidedParameter(niceCamelName name, result.ConvertedType)

// For options and arrays, we need to generate call to ConvertArray or ConvertOption
// (because the child may be represented as primitive type - so we cannot just
Expand Down Expand Up @@ -504,7 +521,7 @@ module internal XmlTypeBuilder =
(result.ConvertedType)
(xml, nameWithNS, convFunc)
),
ProvidedParameter(NameUtils.niceCamelName name, typ)
ProvidedParameter(niceCamelName name, typ)

| InferedMultiplicity.OptionalSingle ->
let convFunc = ReflectionHelpers.makeDelegate result.Converter typeof<XmlElement>
Expand All @@ -523,7 +540,7 @@ module internal XmlTypeBuilder =
(result.ConvertedType.GenericTypeArguments.[0])
(xml, nameWithNS, convFunc)
),
ProvidedParameter(NameUtils.niceCamelName name, result.ConvertedType)
ProvidedParameter(niceCamelName name, result.ConvertedType)
else
let typ = ctx.MakeOptionType result.ConvertedType

Expand All @@ -539,7 +556,7 @@ module internal XmlTypeBuilder =
(result.ConvertedType)
(xml, nameWithNS, convFunc)
),
ProvidedParameter(NameUtils.niceCamelName name, typ)
ProvidedParameter(niceCamelName name, typ)

| _ ->
failwithf "generateXmlType: Child elements should be named record types, got %A" child ]
Expand Down
Loading
Loading