diff --git a/src/FSharp.MongoDB.Bson/Serialization/Conventions/FSharpRecordConvention.fs b/src/FSharp.MongoDB.Bson/Serialization/Conventions/FSharpRecordConvention.fs index 5472385..3d4b007 100644 --- a/src/FSharp.MongoDB.Bson/Serialization/Conventions/FSharpRecordConvention.fs +++ b/src/FSharp.MongoDB.Bson/Serialization/Conventions/FSharpRecordConvention.fs @@ -45,6 +45,5 @@ type FSharpRecordConvention() = let memberMap = classMap.MapMember(pi) let nrtInfo = nrtContext.Create(pi) if nrtInfo.WriteState = NullabilityState.Nullable then - memberMap.SetDefaultValue(null).SetIsRequired(false) |> ignore - ) + memberMap.SetDefaultValue(null).SetIsRequired(false) |> ignore) | _ -> () diff --git a/src/FSharp.MongoDB.Bson/Serialization/Conventions/UnionCaseConvention.fs b/src/FSharp.MongoDB.Bson/Serialization/Conventions/UnionCaseConvention.fs index fbd1112..f7d79b0 100644 --- a/src/FSharp.MongoDB.Bson/Serialization/Conventions/UnionCaseConvention.fs +++ b/src/FSharp.MongoDB.Bson/Serialization/Conventions/UnionCaseConvention.fs @@ -30,6 +30,8 @@ open MongoDB.Bson.Serialization.Helpers type UnionCaseConvention() = inherit ConventionBase() + static let nrtContext = NullabilityInfoContext() + let tryGetUnionCase (typ:System.Type) = // 8.5.4. Compiled Form of Union Types for Use from Other CLI Languages // A compiled union type U has [o]ne CLI nested type U.C for each non-null union case C. @@ -76,7 +78,12 @@ type UnionCaseConvention() = classMap.MapCreator(del, names) |> ignore // Map each field of the union case. - fields |> Array.iter (classMap.MapMember >> ignore) + // Map each field of the record type. + fields |> Array.iter (fun pi -> + let memberMap = classMap.MapMember(pi) + let nrtInfo = nrtContext.Create(pi) + if nrtInfo.WriteState = NullabilityState.Nullable then + memberMap.SetDefaultValue(null).SetIsRequired(false) |> ignore) interface IClassMapConvention with member _.Apply classMap = diff --git a/tests/FSharp.MongoDB.Bson.Tests/Serialization/FSharpNRTSerializationTests.fs b/tests/FSharp.MongoDB.Bson.Tests/Serialization/FSharpNRTSerializationTests.fs index a4f118b..142a44b 100644 --- a/tests/FSharp.MongoDB.Bson.Tests/Serialization/FSharpNRTSerializationTests.fs +++ b/tests/FSharp.MongoDB.Bson.Tests/Serialization/FSharpNRTSerializationTests.fs @@ -19,22 +19,39 @@ open FsUnit open NUnit.Framework module FSharpNRTSerialization = + + type UnionCase = + | Tuple of Int:int * String:(string | null) + + type UnionCaseNotNull = + | TupleNotNull of Int:int * String:string + type Primitive = - { String : string | null + { String : string | null + UnionCase: UnionCase Int: int } - type PrimitiveNoNull = + type RecordNoNull = { StringNotNull : string Int: int } + type UnionCaseNoNull = + { UnionCaseNotNull : UnionCaseNotNull + Int: int } + [] let ``test serialize nullable reference (null) in a record type``() = let value = { String = null + UnionCase = Tuple(Int = 42, String = null) Int = 42 } let result = serialize value + printfn $"{result}" let expected = BsonDocument([ BsonElement("String", BsonNull.Value) + BsonElement("UnionCase", BsonDocument([ BsonElement("_t", BsonString "Tuple") + BsonElement("Int", BsonInt32 42) + BsonElement("String", BsonNull.Value) ])) BsonElement("Int", BsonInt32 42) ]) result |> should equal expected @@ -43,10 +60,14 @@ module FSharpNRTSerialization = let ``test deserialize nullable reference (null) in a record type)``() = // FIXME: once .net 9.0.200 is released, String can be omitted let doc = BsonDocument([ BsonElement("String", BsonNull.Value) + BsonElement("UnionCase", BsonDocument([ BsonElement("_t", BsonString "Tuple") + BsonElement("Int", BsonInt32 42) + BsonElement("String", BsonNull.Value) ])) BsonElement("Int", BsonInt32 42) ]) let result = deserialize doc let expected = { String = null + UnionCase = Tuple(Int = 42, String = null) Int = 42 } result |> should equal expected @@ -54,10 +75,14 @@ module FSharpNRTSerialization = [] let ``test serialize nullable reference (some) in a record type``() = let value = { String = "A String" + UnionCase = Tuple(Int = 42, String = "Another String") Int = 42 } let result = serialize value let expected = BsonDocument([ BsonElement("String", BsonString "A String") + BsonElement("UnionCase", BsonDocument([ BsonElement("_t", BsonString "Tuple") + BsonElement("Int", BsonInt32 42) + BsonElement("String", BsonString "Another String") ])) BsonElement("Int", BsonInt32 42) ]) result |> should equal expected @@ -65,17 +90,28 @@ module FSharpNRTSerialization = [] let ``test deserialize nullable reference (some) in a record type``() = let doc = BsonDocument([ BsonElement("String", BsonString "A String") + BsonElement("UnionCase", BsonDocument([ BsonElement("_t", BsonString "Tuple") + BsonElement("Int", BsonInt32 42) + BsonElement("String", BsonString "Another String") ])) BsonElement("Int", BsonInt32 42) ]) let result = deserialize doc let expected = { String = "A String" + UnionCase = Tuple(Int = 42, String = "Another String") Int = 42 } result |> should equal expected [] - let ``test deserialize with missing non-null reference shall fail``() = + let ``test deserialize with missing non-null reference in record shall fail``() = + let doc = BsonDocument([ BsonElement("Int", BsonInt32 42) ]) + + (fun () -> deserialize doc |> ignore) + |> should throw typeof + + [] + let ``test deserialize with missing non-null reference in union case shall fail``() = let doc = BsonDocument([ BsonElement("Int", BsonInt32 42) ]) - (fun () -> deserialize doc |> ignore) + (fun () -> deserialize doc |> ignore) |> should throw typeof