Skip to content

Commit bef23df

Browse files
committed
Fix #87: check null fields before Deserialize so value types don't fail
1 parent dfc6eb4 commit bef23df

5 files changed

Lines changed: 36 additions & 17 deletions

File tree

src/FSharp.SystemTextJson/Record.fs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,13 @@ type JsonRecordConverter<'T>(options: JsonSerializerOptions, fsOptions: JsonFSha
124124
| ValueSome (i, p) when not p.Ignore ->
125125
if p.MustBePresent then
126126
requiredFieldCount <- requiredFieldCount + 1
127-
fields.[i] <- JsonSerializer.Deserialize(&reader, p.Type, options)
128127

129-
if p.MustBeNonNull && isNull fields.[i] then
128+
reader.Read() |> ignore
129+
if p.MustBeNonNull && reader.TokenType = JsonTokenType.Null then
130130
let msg = sprintf "%s.%s was expected to be of type %s, but was null." recordType.Name p.Name p.Type.Name
131131
raise (JsonException msg)
132+
else
133+
fields.[i] <- JsonSerializer.Deserialize(&reader, p.Type, options)
132134
| _ ->
133135
reader.Skip()
134136
| _ -> ()

src/FSharp.SystemTextJson/Union.fs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -241,17 +241,17 @@ type JsonUnionConverter<'T>
241241
| false, _ -> ValueNone
242242

243243
let readField (reader: byref<Utf8JsonReader>) (case: Case) (f: Field) options =
244-
let v = JsonSerializer.Deserialize(&reader, f.Type, options)
245-
if isNull v && f.MustBeNonNull then
244+
reader.Read() |> ignore
245+
if f.MustBeNonNull && reader.TokenType = JsonTokenType.Null then
246246
let msg = sprintf "%s.%s(%s) was expected to be of type %s, but was null." ty.Name case.Name f.Name f.Type.Name
247247
raise (JsonException msg)
248-
v
248+
else
249+
JsonSerializer.Deserialize(&reader, f.Type, options)
249250

250251
let readFieldsAsRestOfArray (reader: byref<Utf8JsonReader>) (case: Case) (options: JsonSerializerOptions) =
251252
let fieldCount = case.Fields.Length
252253
let fields = Array.zeroCreate fieldCount
253254
for i in 0..fieldCount-1 do
254-
reader.Read() |> ignore
255255
fields.[i] <- readField &reader case case.Fields.[i] options
256256
readExpecting JsonTokenType.EndArray "end of array" &reader ty
257257
case.Ctor fields :?> 'T

tests/FSharp.SystemTextJson.Tests/FSharp.SystemTextJson.Tests.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<Compile Include="Test.Collection.fs" />
1010
<Compile Include="Test.Record.fs" />
1111
<Compile Include="Test.Union.fs" />
12+
<Compile Include="Test.Regression.fs" />
1213
<Compile Include="Program.fs" />
1314
<None Include="paket.references" />
1415
</ItemGroup>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
module Tests.Regression
2+
3+
open Xunit
4+
open System.Text.Json.Serialization
5+
open System.Text.Json
6+
7+
type Color = Red | Blue | Green
8+
9+
[<Fact>]
10+
let ``regression #33`` () =
11+
let serializerOptions = JsonSerializerOptions()
12+
serializerOptions.Converters.Add(JsonFSharpConverter(JsonUnionEncoding.UnwrapFieldlessTags))
13+
let actual = JsonSerializer.Deserialize<Color list>("""[ "Red", "Blue"] """, serializerOptions)
14+
Assert.Equal<Color>([Red; Blue], actual)
15+
let actual = JsonSerializer.Deserialize("""{"a":"Red","b":"Blue"}""", serializerOptions)
16+
Assert.Equal({| a = Red; b = Blue |}, actual)
17+
18+
type A = { A: int; B: string }
19+
20+
[<Fact>]
21+
let ``regression #87`` () =
22+
let options = JsonSerializerOptions()
23+
options.Converters.Add(JsonFSharpConverter())
24+
let ex1 = Assert.Throws<JsonException>(fun () -> JsonSerializer.Deserialize<A>("""{ "A": 2, "B": null }""", options) |> ignore)
25+
Assert.Equal("A.B was expected to be of type String, but was null.", ex1.Message)
26+
let ex2 = Assert.Throws<JsonException>(fun () -> JsonSerializer.Deserialize<A>("""{ "A": null, "B": "a" }""", options) |> ignore)
27+
Assert.Equal("A.A was expected to be of type Int32, but was null.", ex2.Message)

tests/FSharp.SystemTextJson.Tests/Test.Union.fs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -494,17 +494,6 @@ module NonStruct =
494494
let ``serialize non-unwrapped single-case`` () =
495495
Assert.Equal("""{"Case":"Unwrapped","Fields":["foo"]}""", JsonSerializer.Serialize(Unwrapped "foo", noNewtypeOptions))
496496

497-
type Color = Red | Blue | Green
498-
499-
[<Fact>]
500-
let ``regression #33`` () =
501-
let serializerOptions = JsonSerializerOptions()
502-
serializerOptions.Converters.Add(JsonFSharpConverter(JsonUnionEncoding.UnwrapFieldlessTags))
503-
let actual = JsonSerializer.Deserialize<Color list>("""[ "Red", "Blue"] """, serializerOptions)
504-
Assert.Equal<Color>([Red; Blue], actual)
505-
let actual = JsonSerializer.Deserialize("""{"a":"Red","b":"Blue"}""", serializerOptions)
506-
Assert.Equal({| a = Red; b = Blue |}, actual)
507-
508497
type UnionWithUnitField =
509498
| UWUF of int * unit
510499

0 commit comments

Comments
 (0)