Skip to content

Commit 572e162

Browse files
Repo AssistCopilot
authored andcommitted
Resolve merge conflicts with main: integrate OmitNullFields into With* PR
Main merged the OmitNullFields PR (#1638) after this branch was created. Resolving conflicts by integrating both sets of changes: - JsonGenerator.fs: add OmitNullFields param + keep With* method generation - JsonProvider.fs: accept new OmitNullFields static parameter - JsonRuntime.fs: add both WithRecordProperty and CreateRecordOmitNulls - TypeProviderInstantiation.fs: update test struct with OmitNullFields - JsonProvider tests: include OmitNullFields tests alongside With* tests Build: 0 errors. Tests: 268/268 passed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 80988bd commit 572e162

5 files changed

Lines changed: 94 additions & 13 deletions

File tree

src/FSharp.Data.DesignTime/Json/JsonGenerator.fs

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ type internal JsonGenerationContext =
3131
GenerateConstructors: bool
3232
InferenceMode: InferenceMode'
3333
UnitsOfMeasureProvider: IUnitsOfMeasureProvider
34-
UseOriginalNames: bool }
34+
UseOriginalNames: bool
35+
OmitNullFields: bool }
3536

3637
static member Create
3738
(
@@ -42,7 +43,8 @@ type internal JsonGenerationContext =
4243
?uniqueNiceName,
4344
?typeCache,
4445
?preferDictionaries,
45-
?useOriginalNames
46+
?useOriginalNames,
47+
?omitNullFields
4648
) =
4749
let useOriginalNames = defaultArg useOriginalNames false
4850

@@ -53,6 +55,7 @@ type internal JsonGenerationContext =
5355

5456
let typeCache = defaultArg typeCache (Dictionary())
5557
let preferDictionaries = defaultArg preferDictionaries false
58+
let omitNullFields = defaultArg omitNullFields false
5659

5760
JsonGenerationContext.Create(
5861
cultureStr,
@@ -63,7 +66,8 @@ type internal JsonGenerationContext =
6366
true,
6467
inferenceMode,
6568
unitsOfMeasureProvider,
66-
useOriginalNames
69+
useOriginalNames,
70+
omitNullFields
6771
)
6872

6973
static member Create
@@ -76,7 +80,8 @@ type internal JsonGenerationContext =
7680
generateConstructors,
7781
inferenceMode,
7882
unitsOfMeasureProvider,
79-
useOriginalNames
83+
useOriginalNames,
84+
omitNullFields
8085
) =
8186
{ CultureStr = cultureStr
8287
TypeProviderType = tpType
@@ -89,7 +94,33 @@ type internal JsonGenerationContext =
8994
GenerateConstructors = generateConstructors
9095
InferenceMode = inferenceMode
9196
UnitsOfMeasureProvider = unitsOfMeasureProvider
92-
UseOriginalNames = useOriginalNames }
97+
UseOriginalNames = useOriginalNames
98+
OmitNullFields = omitNullFields }
99+
100+
static member Create
101+
(
102+
cultureStr,
103+
tpType,
104+
uniqueNiceName,
105+
typeCache,
106+
preferDictionaries,
107+
generateConstructors,
108+
inferenceMode,
109+
unitsOfMeasureProvider,
110+
useOriginalNames
111+
) =
112+
JsonGenerationContext.Create(
113+
cultureStr,
114+
tpType,
115+
uniqueNiceName,
116+
typeCache,
117+
preferDictionaries,
118+
generateConstructors,
119+
inferenceMode,
120+
unitsOfMeasureProvider,
121+
useOriginalNames,
122+
false
123+
)
93124

94125
member x.MakeOptionType(typ: Type) =
95126
typedefof<option<_>>.MakeGenericType typ
@@ -646,7 +677,11 @@ module JsonTypeBuilder =
646677
)
647678

648679
let cultureStr = ctx.CultureStr
649-
<@@ JsonRuntime.CreateRecord(%%properties, cultureStr) @@>
680+
681+
if ctx.OmitNullFields then
682+
<@@ JsonRuntime.CreateRecordOmitNulls(%%properties, cultureStr) @@>
683+
else
684+
<@@ JsonRuntime.CreateRecord(%%properties, cultureStr) @@>
650685

651686
let ctor = ProvidedConstructor(parameters, invokeCode = ctorCode)
652687
objectTy.AddMember ctor

src/FSharp.Data.DesignTime/Json/JsonProvider.fs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ type public JsonProvider(cfg: TypeProviderConfig) as this =
5959
let schema = args.[10] :?> string
6060
let preferDateOnly = args.[11] :?> bool
6161
let useOriginalNames = args.[12] :?> bool
62+
let omitNullFields = args.[13] :?> bool
6263

6364
let inferenceMode =
6465
InferenceMode'.FromPublicApi(inferenceMode, inferTypesFromValues)
@@ -119,7 +120,8 @@ type public JsonProvider(cfg: TypeProviderConfig) as this =
119120
unitsOfMeasureProvider,
120121
inferenceMode,
121122
?preferDictionaries = Some preferDictionaries,
122-
?useOriginalNames = Some useOriginalNames
123+
?useOriginalNames = Some useOriginalNames,
124+
?omitNullFields = Some omitNullFields
123125
)
124126

125127
let result = JsonTypeBuilder.generateJsonType ctx false false rootName inferedType
@@ -167,7 +169,8 @@ type public JsonProvider(cfg: TypeProviderConfig) as this =
167169
)
168170
ProvidedStaticParameter("Schema", typeof<string>, parameterDefaultValue = "")
169171
ProvidedStaticParameter("PreferDateOnly", typeof<bool>, parameterDefaultValue = false)
170-
ProvidedStaticParameter("UseOriginalNames", typeof<bool>, parameterDefaultValue = false) ]
172+
ProvidedStaticParameter("UseOriginalNames", typeof<bool>, parameterDefaultValue = false)
173+
ProvidedStaticParameter("OmitNullFields", typeof<bool>, parameterDefaultValue = false) ]
171174

172175
let helpText =
173176
"""<summary>Typed representation of a JSON document.</summary>
@@ -192,7 +195,8 @@ type public JsonProvider(cfg: TypeProviderConfig) as this =
192195
</param>
193196
<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>
194197
<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>
195-
<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>"""
198+
<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>
199+
<param name='OmitNullFields'>When true, optional fields with value None are omitted from the generated JSON rather than serialized as null. Defaults to false.</param>"""
196200

197201
do jsonProvTy.AddXmlDoc helpText
198202
do jsonProvTy.DefineStaticParameters(parameters, buildTypes)

src/FSharp.Data.Json.Core/JsonRuntime.fs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,21 @@ type JsonRuntime =
375375
Array.append updatedProps [| name, newPropValue |]
376376

377377
JsonDocument.Create(JsonValue.Record finalProps, "")
378+
// Creates a JsonValue.Record, omitting null fields, and wraps it in a json document
379+
static member CreateRecordOmitNulls(properties, cultureStr) =
380+
let cultureInfo = TextRuntime.GetCulture cultureStr
381+
382+
let json =
383+
properties
384+
|> Array.choose (fun (k, v: obj) ->
385+
let jv = JsonRuntime.ToJsonValue cultureInfo v
386+
387+
match jv with
388+
| JsonValue.Null -> None
389+
| _ -> Some(k, jv))
390+
|> JsonValue.Record
391+
392+
JsonDocument.Create(json, "")
378393

379394
// Creates a JsonValue.Record from key*value seq and wraps it in a json document
380395
static member CreateRecordFromDictionary<'Key, 'Value when 'Key: equality>

tests/FSharp.Data.DesignTime.Tests/TypeProviderInstantiation.fs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ type internal JsonProviderArgs =
5757
InferenceMode: InferenceMode
5858
Schema: string
5959
PreferDateOnly : bool
60-
UseOriginalNames : bool }
60+
UseOriginalNames : bool
61+
OmitNullFields : bool }
6162

6263
type internal HtmlProviderArgs =
6364
{ Sample : string
@@ -133,7 +134,8 @@ type internal TypeProviderInstantiation =
133134
box x.InferenceMode
134135
box x.Schema
135136
box x.PreferDateOnly
136-
box x.UseOriginalNames |]
137+
box x.UseOriginalNames
138+
box x.OmitNullFields |]
137139
| Html x ->
138140
(fun cfg -> new HtmlProvider(cfg) :> TypeProviderForNamespaces),
139141
[| box x.Sample
@@ -271,7 +273,8 @@ type internal TypeProviderInstantiation =
271273
InferenceMode = args.[7] |> InferenceMode.Parse
272274
Schema = if args.Length > 8 then args.[8] else ""
273275
PreferDateOnly = false
274-
UseOriginalNames = false }
276+
UseOriginalNames = false
277+
OmitNullFields = false }
275278
else
276279
// This is for schema-based tests in the format "Json,,,,,true,false,BackwardCompatible,SimpleSchema.json"
277280
Json { Sample = args.[1]
@@ -286,7 +289,8 @@ type internal TypeProviderInstantiation =
286289
InferenceMode = InferenceMode.Parse "BackwardCompatible"
287290
Schema = if args.Length > 8 then args.[8] else ""
288291
PreferDateOnly = false
289-
UseOriginalNames = false }
292+
UseOriginalNames = false
293+
OmitNullFields = false }
290294
| "Html" ->
291295
Html { Sample = args.[1]
292296
PreferOptionals = args.[2] |> bool.Parse

tests/FSharp.Data.Tests/JsonProvider.fs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -994,3 +994,26 @@ let ``JsonProvider With* methods do not mutate the original record`` () =
994994
let doc = JsonWithMethods.Parse("""{"name": "Alice", "age": 30, "active": true}""")
995995
let _ = doc.WithName("Bob")
996996
doc.Name |> should equal "Alice"
997+
type JsonOmitNullFieldsSample = JsonProvider<"""[{"color": "Red", "code": 15}, {"color": "Green"}]""", SampleIsList = true, OmitNullFields = true>
998+
type JsonIncludeNullFieldsSample = JsonProvider<"""[{"color": "Red", "code": 15}, {"color": "Green"}]""", SampleIsList = true>
999+
1000+
[<Test>]
1001+
let ``JsonProvider OmitNullFields=true omits None optional fields from output`` () =
1002+
let value = JsonOmitNullFieldsSample.Root(color = "Blue", code = None)
1003+
let json = value.ToString()
1004+
json |> should not' (contain "null")
1005+
json |> should not' (contain "code")
1006+
json |> should contain "Blue"
1007+
1008+
[<Test>]
1009+
let ``JsonProvider default includes None optional fields as null`` () =
1010+
let value = JsonIncludeNullFieldsSample.Root(color = "Blue", code = None)
1011+
let json = value.ToString()
1012+
json |> should contain "null"
1013+
1014+
[<Test>]
1015+
let ``JsonProvider OmitNullFields=true includes non-None fields`` () =
1016+
let value = JsonOmitNullFieldsSample.Root(color = "Blue", code = Some 42)
1017+
let json = value.ToString()
1018+
json |> should contain "42"
1019+
json |> should contain "Blue"

0 commit comments

Comments
 (0)