Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ All notable changes to FSharp.MongoDB are documented in this file.
## [Unreleased]

- Updated the test harness projects to target .NET 10 so local and CI test execution stays aligned with the repository SDK baseline.
- Fixed F# map support for `BsonDictionaryOptions`, including `ArrayOfDocuments` coverage.

## [0.5.0-beta]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,20 @@
namespace MongoDB.Bson.Serialization.Serializers

open System.Collections.Generic
open MongoDB.Bson.Serialization
open MongoDB.Bson.Serialization.Options
open MongoDB.Bson.Serialization.Serializers

/// <summary>
/// Serializer for F# maps.
/// </summary>
type FSharpMapSerializer<'KeyType, 'ValueType when 'KeyType : comparison and 'KeyType: not null>() =
type FSharpMapSerializer<'KeyType, 'ValueType when 'KeyType : comparison and 'KeyType: not null>
(
serializer:
DictionaryInterfaceImplementerSerializer<Dictionary<'KeyType, 'ValueType>, 'KeyType, 'ValueType>
) =
inherit SerializerBase<Map<'KeyType, 'ValueType>>()

let serializer = DictionaryInterfaceImplementerSerializer<Dictionary<'KeyType, 'ValueType>>()

override _.Serialize (context, args, mapValue) =
let dictValue = Dictionary()
mapValue |> Map.iter (fun key value -> dictValue.Add(key, value))
Expand All @@ -36,3 +40,23 @@ type FSharpMapSerializer<'KeyType, 'ValueType when 'KeyType : comparison and 'Ke
serializer.Deserialize(context, args)
|> Seq.map (|KeyValue|)
|> Map.ofSeq

interface IBsonDictionarySerializer with
member _.DictionaryRepresentation = serializer.DictionaryRepresentation
member _.KeySerializer = serializer.KeySerializer :> IBsonSerializer
member _.ValueSerializer = serializer.ValueSerializer :> IBsonSerializer

interface IDictionaryRepresentationConfigurable with
member _.DictionaryRepresentation = serializer.DictionaryRepresentation
member _.WithDictionaryRepresentation dictionaryRepresentation =
FSharpMapSerializer<'KeyType, 'ValueType>(serializer.WithDictionaryRepresentation(dictionaryRepresentation))
:> IBsonSerializer

interface IDictionaryRepresentationConfigurable<FSharpMapSerializer<'KeyType, 'ValueType>> with
member _.WithDictionaryRepresentation dictionaryRepresentation =
FSharpMapSerializer<'KeyType, 'ValueType>(serializer.WithDictionaryRepresentation(dictionaryRepresentation))

new () =
FSharpMapSerializer<'KeyType, 'ValueType>(
DictionaryInterfaceImplementerSerializer<Dictionary<'KeyType, 'ValueType>, 'KeyType, 'ValueType>()
)
10 changes: 10 additions & 0 deletions tests/CSharpDataModels/DataModels.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
namespace CSharpDataModels;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson.Serialization.Options;

public record Pair
{
Expand Down Expand Up @@ -40,3 +42,11 @@ public record RecordDataModel

public required Dictionary<string, int> Map { get; init; }
}

public record UserId([property: BsonGuidRepresentation(GuidRepresentation.Standard)] Guid Value);

public record UserMapDocument
{
[BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)]
public required Dictionary<UserId, string> Users { get; init; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<Compile Include="SerializationTestHelpers.fs" />
<Compile Include="Serialization\FSharpListSerializationTests.fs" />
<Compile Include="Serialization\FSharpMapSerializationTests.fs" />
<Compile Include="Serialization\FSharpMapDictionaryRepresentationTests.fs" />
<Compile Include="Serialization\FSharpOptionSerializationTests.fs" />
<Compile Include="Serialization\FSharpNRTSerializationTests.fs" />
<Compile Include="Serialization\FSharpValueOptionSerializationTests.fs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
namespace FSharp.MongoDB.Bson.Tests.Serialization

open System
open CSharpDataModels
open FsUnit
open MongoDB.Bson
open MongoDB.Bson.Serialization.Attributes
open MongoDB.Bson.Serialization.Options
open NUnit.Framework

module FSharpMapDictionaryRepresentation =

[<CLIMutable>]
type FsUserId =
{ [<BsonGuidRepresentation(GuidRepresentation.Standard)>]
Value: Guid }

[<CLIMutable>]
type FsUserMapDocument =
{ [<BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)>]
Users: Map<FsUserId, string> }

let private userGuid = Guid.Parse("550e8400-e29b-41d4-a716-446655440000")

let private expectedDocument =
BsonDocument(
[ BsonElement(
"Users",
BsonArray(
[ BsonDocument(
[ BsonElement("k", BsonDocument("Value", BsonBinaryData(userGuid, GuidRepresentation.Standard)))
BsonElement("v", BsonString("John")) ]) ])) ])

[<Test>]
let ``serialize map with complex key as array of documents`` () =
let value =
{ Users =
Map [ { Value = userGuid }, "John" ] }

let result = serialize value
result |> should equal expectedDocument

[<Test>]
let ``deserialize map with complex key from array of documents`` () =
let result = deserialize<FsUserMapDocument> expectedDocument

result.Users
|> should equal (Map [ { Value = userGuid }, "John" ])

[<Test>]
let ``CSharp and FSharp models serialize the same array of documents representation`` () =
let csUsers =
System.Collections.Generic.Dictionary<CSharpDataModels.UserId, string>(
dict [ (CSharpDataModels.UserId(userGuid), "John") ])

let csValue =
new CSharpDataModels.UserMapDocument(Users = csUsers)

let fsValue =
{ Users =
Map [ { Value = userGuid }, "John" ] }

let csDoc = serialize csValue
let fsDoc = serialize fsValue

csDoc |> should equal expectedDocument
fsDoc |> should equal expectedDocument

[<Test>]
let ``cross deserialize map with complex key using array of documents`` () =
let csUsers =
System.Collections.Generic.Dictionary<CSharpDataModels.UserId, string>(
dict [ (CSharpDataModels.UserId(userGuid), "John") ])

let csDoc =
serialize (new CSharpDataModels.UserMapDocument(Users = csUsers))

let fsValue = deserialize<FsUserMapDocument> csDoc
fsValue.Users |> should equal (Map [ { Value = userGuid }, "John" ])

let fsDoc =
serialize
{ Users =
Map [ { Value = userGuid }, "John" ] }

let csValue = deserialize<CSharpDataModels.UserMapDocument> fsDoc
csValue.Users.Count |> should equal 1
csValue.Users[CSharpDataModels.UserId(userGuid)] |> should equal "John"
Loading