diff --git a/FSharp.MongoDB.Driver.Tests/AcceptanceTests.fs b/FSharp.MongoDB.Driver.Tests/AcceptanceTests.fs index a3f1e60..cfcdce0 100644 --- a/FSharp.MongoDB.Driver.Tests/AcceptanceTests.fs +++ b/FSharp.MongoDB.Driver.Tests/AcceptanceTests.fs @@ -1,269 +1,269 @@ -module FSharp.MongoDB.Driver.Tests - -open System -open FsUnit -open NUnit.Framework -open MongoDB.Bson -open MongoDB.Driver -open System.Linq -open TestUtils -open MongoDB.Bson.Serialization.Attributes - -type ObjectWithList() = - member val Id : BsonObjectId = newBsonObjectId() with get, set - member val List : string list = [] with get, set - -type RecordType = - { Id : BsonObjectId - Name : string } - -[] -[] -type RecordTypeOptId = - { [] Id : ObjectId - Name : string } - -type Child = - { ChildName: string - Age: int } - -type Person = - { Id: BsonObjectId - PersonName: string - Age: int - Childs: Child seq } - -type DimmerSwitch = - | Off - | Dim of int - | DimMarquee of int * string - | On - -type RecordWithCollections = - { Id: BsonObjectId - IntVal: int - DoubleVal: double - ListVal: int list - IntValOpt: int ValueOption - SetVal: Set option - MapVal: Map option - OptionVal: int option } - -type ObjectWithDimmer() = - member val Id : BsonObjectId = newBsonObjectId() with get, set - member val Switch : DimmerSwitch = Off with get, set - -type ObjectWithDimmers() = - member val Id : BsonObjectId = newBsonObjectId() with get, set - member val Kitchen : DimmerSwitch = Off with get, set - member val Bedroom1 : DimmerSwitch = Off with get, set - member val Bedroom2 : DimmerSwitch = Off with get, set - -type ObjectWithOptions() = - member val Id : BsonObjectId = newBsonObjectId() with get, set - member val Age : int option = None with get, set - -let mutable client: MongoClient = Unchecked.defaultof -let mutable db: IMongoDatabase = Unchecked.defaultof - - -[] -let init() = - let connectionString = "mongodb://localhost" - let dbname = "FSharp-MongoDB-Driver" - client <- new MongoClient(connectionString) - client.DropDatabase(dbname) - db <- client.GetDatabase(dbname) - Register() - -[] -let teardown() = - client.Dispose() - - -[] -let ``It can serialize an object with a list``() = - let collection = db.GetCollection "ObjectWithList" - let obj = ObjectWithList(List = [ "hello"; "world" ]) - collection.InsertOne(obj) - - let genCollection = db.GetCollection "ObjectWithList" - let fromDb = genCollection.Find(fun x -> x.Id = obj.Id).ToList().First() - let array = fromDb.List - array.Length |> should equal 2 - -[] -let ``It can deserialze lists``() = - let list = BsonArray([ "hello"; "world" ]) - let id = newBsonObjectId() - let document = BsonDocument([ BsonElement("_id", id); BsonElement("List", list) ]) - let collection = db.GetCollection "ObjectWithList" - collection.InsertOne document - - let collection = db.GetCollection "ObjectWithList" - let fromDb = collection.Find(fun x -> x.Id = id).ToList().First() - let array = fromDb.List - array.Length |> should equal 2 - -[] -let ``It can serialize records``() = - let collection = db.GetCollection "RecordType" - let obj = { Id = newBsonObjectId(); Name = "test" } - collection.InsertOne obj - - let genCollection = db.GetCollection "RecordType" - let fromDb = genCollection |> findById obj.Id - let name = fromDb["Name"].AsString - name |> should equal "test" - -[] -let ``It can serialize records and generate Id``() = - let collection = db.GetCollection "RecordTypeOptId" - let obj = { RecordTypeOptId.Id = Unchecked.defaultof ; RecordTypeOptId.Name = "test" } - collection.InsertOne obj - - let genCollection = db.GetCollection "RecordTypeOptId" - let fromDb = genCollection.Find(fun x -> x.Name = "test").First() - fromDb.Id |> should equal obj.Id - fromDb.Id |> should not' (equal Unchecked.defaultof) - -[] -let ``It can deserialize records``() = - let id = newBsonObjectId() - let document = BsonDocument([BsonElement("_id", id); BsonElement("Name", BsonString("value"))]) - let collection = db.GetCollection "RecordType" - collection.InsertOne(document) - - let collection = db.GetCollection("RecordType") - let fromDb = collection.Find(fun x -> x.Id = id).ToList().First() - Assert.NotNull(fromDb) - fromDb.Name |> should equal "value" - -[] -let ``It can serialize and deserialize nested records``() = - let collection = db.GetCollection "Person" - let obj = { Id = newBsonObjectId(); PersonName = "test"; Age = 33; Childs = [{ChildName = "Adrian"; Age = 3}] } - collection.InsertOne obj - - let genCollection = db.GetCollection "Person" - let person = - query { - for p in genCollection.AsQueryable() do - where (p.Id = obj.Id) - select p - headOrDefault - } - - person |> should not' (be null) - person.PersonName |> should equal "test" - person.Age |> should equal 33 - person.Childs |> Seq.length |> should equal 1 - - let child = person.Childs |> Seq.head - child.ChildName |> should equal "Adrian" - child.Age |> should equal 3 - -[] -let ``It can serialize DimmerSwitch types``() = - let collection = db.GetCollection "ObjectWithDimmer" - let obj = ObjectWithDimmer(Switch = DimMarquee(42, "loser")) - collection.InsertOne obj - - let collection = db.GetCollection "ObjectWithDimmer" - let fromDb = collection |> findById obj.Id - let switch = fromDb.GetElement("Switch") - switch |> should not' (be null) - let value = switch.Value.AsBsonDocument.GetElement("DimMarquee").Value - value.IsBsonArray |> should be True - let array = value.AsBsonArray - array.Count |> should equal 2 - array.[0].AsInt32 |> should equal 42 - array.[1].AsString |> should equal "loser" - -[] -let ``It can serialize option types``() = - let collection = db.GetCollection "ObjectWithOptions" - let obj = ObjectWithOptions(Age = Some 42) - collection.InsertOne obj - - let collection = db.GetCollection "ObjectWithOptions" - let fromDb = collection |> findById obj.Id - let age = fromDb.GetElement("Age") - let v = age.Value - v.AsInt32 |> should equal 42 - -[] -let ``It can serialize option types with None``() = - let collection = db.GetCollection "ObjectWithOptions" - let obj = ObjectWithOptions(Age = None) - collection.InsertOne obj - - let collection = db.GetCollection "ObjectWithOptions" - let fromDb = collection |> findById obj.Id - let age = fromDb.GetElement("Age") - let v = age.Value - v.AsBsonNull |> should equal BsonNull.Value - -[] -let ``It can deserialize option types``() = - let collection = db.GetCollection "ObjectWithOptions" - let document = ObjectWithOptions(Id = newBsonObjectId(), Age = Some 42) - collection.InsertOne document - - let collection = db.GetCollection "ObjectWithOptions" - let fromDb = collection.Find(fun x -> x.Id = document.Id).ToList().First() - match fromDb.Age with - | Some 42 -> () - | _ -> failwith "expected Some 42 but got something else" - -[] -let ``It can deserialize option types from undefined``() = - let id = newBsonObjectId() - let document = BsonDocument([BsonElement("_id", id)]) - let collection = db.GetCollection "ObjectWithOptions" - collection.InsertOne document - - let collection = db.GetCollection "ObjectWithOptions" - let fromDb = collection.Find(fun x -> x.Id = id).ToList().First() - fromDb.Age |> should equal None - -[] -let ``We can integrate serialize & deserialize on DimmerSwitches``() = - let collection = db.GetCollection "ObjectWithDimmers" - let obj = ObjectWithDimmers(Kitchen = Off, - Bedroom1 = Dim 42, - Bedroom2 = DimMarquee(12, "when I was little...")) - collection.InsertOne obj - - let fromDb = collection.Find(fun x -> x.Id = obj.Id).ToList().First() - match fromDb.Kitchen with - | Off -> () - | _ -> failwith "Kitchen light wasn't off" - - match fromDb.Bedroom1 with - | Dim 42 -> () - | _ -> failwith "Bedroom1 light wasn't dim enough" - - match fromDb.Bedroom2 with - | DimMarquee(12, "when I was little...") -> () - | _ -> failwith "Bedroom2 doesn't have the party we thought" - -[] -let ``It can serialize record with list`` () = - let collection = db.GetCollection "RecordWithCollections" - let obj = - { Id = newBsonObjectId() - IntVal = 123 - DoubleVal = 1.23 - ListVal = [1; 2; 3] - IntValOpt = ValueSome 42 - SetVal = ["toto"; "titi"; "tata"] |> Set |> Some - MapVal = ["toto", 42; "titi", 666] |> Map |> Some - OptionVal = Some 123 } - collection.InsertOne obj - - let testCollection = db.GetCollection "RecordWithCollections" - Console.WriteLine((testCollection |> findById obj.Id).ToJson()) - - let fromDb = collection.Find(fun x -> x.Id = obj.Id).ToList().First() - fromDb |> should equal obj +// module FSharp.MongoDB.Driver.Tests +// +// open System +// open FsUnit +// open NUnit.Framework +// open MongoDB.Bson +// open MongoDB.Driver +// open System.Linq +// open TestUtils +// open MongoDB.Bson.Serialization.Attributes +// +// type ObjectWithList() = +// member val Id : BsonObjectId = newBsonObjectId() with get, set +// member val List : string list = [] with get, set +// +// type RecordType = +// { Id : BsonObjectId +// Name : string } +// +// [] +// [] +// type RecordTypeOptId = +// { [] Id : ObjectId +// Name : string } +// +// type Child = +// { ChildName: string +// Age: int } +// +// type Person = +// { Id: BsonObjectId +// PersonName: string +// Age: int +// Childs: Child seq } +// +// type DimmerSwitch = +// | Off +// | Dim of int +// | DimMarquee of int * string +// | On +// +// type RecordWithCollections = +// { Id: BsonObjectId +// IntVal: int +// DoubleVal: double +// ListVal: int list +// IntValOpt: int ValueOption +// SetVal: Set option +// MapVal: Map option +// OptionVal: int option } +// +// type ObjectWithDimmer() = +// member val Id : BsonObjectId = newBsonObjectId() with get, set +// member val Switch : DimmerSwitch = Off with get, set +// +// type ObjectWithDimmers() = +// member val Id : BsonObjectId = newBsonObjectId() with get, set +// member val Kitchen : DimmerSwitch = Off with get, set +// member val Bedroom1 : DimmerSwitch = Off with get, set +// member val Bedroom2 : DimmerSwitch = Off with get, set +// +// type ObjectWithOptions() = +// member val Id : BsonObjectId = newBsonObjectId() with get, set +// member val Age : int option = None with get, set +// +// let mutable client: MongoClient = Unchecked.defaultof +// let mutable db: IMongoDatabase = Unchecked.defaultof +// +// +// [] +// let init() = +// let connectionString = "mongodb://localhost" +// let dbname = "FSharp-MongoDB-Driver" +// client <- new MongoClient(connectionString) +// client.DropDatabase(dbname) +// db <- client.GetDatabase(dbname) +// Register() +// +// [] +// let teardown() = +// client.Dispose() +// +// +// [] +// let ``It can serialize an object with a list``() = +// let collection = db.GetCollection "ObjectWithList" +// let obj = ObjectWithList(List = [ "hello"; "world" ]) +// collection.InsertOne(obj) +// +// let genCollection = db.GetCollection "ObjectWithList" +// let fromDb = genCollection.Find(fun x -> x.Id = obj.Id).ToList().First() +// let array = fromDb.List +// array.Length |> should equal 2 +// +// [] +// let ``It can deserialze lists``() = +// let list = BsonArray([ "hello"; "world" ]) +// let id = newBsonObjectId() +// let document = BsonDocument([ BsonElement("_id", id); BsonElement("List", list) ]) +// let collection = db.GetCollection "ObjectWithList" +// collection.InsertOne document +// +// let collection = db.GetCollection "ObjectWithList" +// let fromDb = collection.Find(fun x -> x.Id = id).ToList().First() +// let array = fromDb.List +// array.Length |> should equal 2 +// +// [] +// let ``It can serialize records``() = +// let collection = db.GetCollection "RecordType" +// let obj = { Id = newBsonObjectId(); Name = "test" } +// collection.InsertOne obj +// +// let genCollection = db.GetCollection "RecordType" +// let fromDb = genCollection |> findById obj.Id +// let name = fromDb["Name"].AsString +// name |> should equal "test" +// +// [] +// let ``It can serialize records and generate Id``() = +// let collection = db.GetCollection "RecordTypeOptId" +// let obj = { RecordTypeOptId.Id = Unchecked.defaultof ; RecordTypeOptId.Name = "test" } +// collection.InsertOne obj +// +// let genCollection = db.GetCollection "RecordTypeOptId" +// let fromDb = genCollection.Find(fun x -> x.Name = "test").First() +// fromDb.Id |> should equal obj.Id +// fromDb.Id |> should not' (equal Unchecked.defaultof) +// +// [] +// let ``It can deserialize records``() = +// let id = newBsonObjectId() +// let document = BsonDocument([BsonElement("_id", id); BsonElement("Name", BsonString("value"))]) +// let collection = db.GetCollection "RecordType" +// collection.InsertOne(document) +// +// let collection = db.GetCollection("RecordType") +// let fromDb = collection.Find(fun x -> x.Id = id).ToList().First() +// Assert.NotNull(fromDb) +// fromDb.Name |> should equal "value" +// +// [] +// let ``It can serialize and deserialize nested records``() = +// let collection = db.GetCollection "Person" +// let obj = { Id = newBsonObjectId(); PersonName = "test"; Age = 33; Childs = [{ChildName = "Adrian"; Age = 3}] } +// collection.InsertOne obj +// +// let genCollection = db.GetCollection "Person" +// let person = +// query { +// for p in genCollection.AsQueryable() do +// where (p.Id = obj.Id) +// select p +// headOrDefault +// } +// +// person |> should not' (be null) +// person.PersonName |> should equal "test" +// person.Age |> should equal 33 +// person.Childs |> Seq.length |> should equal 1 +// +// let child = person.Childs |> Seq.head +// child.ChildName |> should equal "Adrian" +// child.Age |> should equal 3 +// +// [] +// let ``It can serialize DimmerSwitch types``() = +// let collection = db.GetCollection "ObjectWithDimmer" +// let obj = ObjectWithDimmer(Switch = DimMarquee(42, "loser")) +// collection.InsertOne obj +// +// let collection = db.GetCollection "ObjectWithDimmer" +// let fromDb = collection |> findById obj.Id +// let switch = fromDb.GetElement("Switch") +// switch |> should not' (be null) +// let value = switch.Value.AsBsonDocument.GetElement("DimMarquee").Value +// value.IsBsonArray |> should be True +// let array = value.AsBsonArray +// array.Count |> should equal 2 +// array.[0].AsInt32 |> should equal 42 +// array.[1].AsString |> should equal "loser" +// +// [] +// let ``It can serialize option types``() = +// let collection = db.GetCollection "ObjectWithOptions" +// let obj = ObjectWithOptions(Age = Some 42) +// collection.InsertOne obj +// +// let collection = db.GetCollection "ObjectWithOptions" +// let fromDb = collection |> findById obj.Id +// let age = fromDb.GetElement("Age") +// let v = age.Value +// v.AsInt32 |> should equal 42 +// +// [] +// let ``It can serialize option types with None``() = +// let collection = db.GetCollection "ObjectWithOptions" +// let obj = ObjectWithOptions(Age = None) +// collection.InsertOne obj +// +// let collection = db.GetCollection "ObjectWithOptions" +// let fromDb = collection |> findById obj.Id +// let age = fromDb.GetElement("Age") +// let v = age.Value +// v.AsBsonNull |> should equal BsonNull.Value +// +// [] +// let ``It can deserialize option types``() = +// let collection = db.GetCollection "ObjectWithOptions" +// let document = ObjectWithOptions(Id = newBsonObjectId(), Age = Some 42) +// collection.InsertOne document +// +// let collection = db.GetCollection "ObjectWithOptions" +// let fromDb = collection.Find(fun x -> x.Id = document.Id).ToList().First() +// match fromDb.Age with +// | Some 42 -> () +// | _ -> failwith "expected Some 42 but got something else" +// +// [] +// let ``It can deserialize option types from undefined``() = +// let id = newBsonObjectId() +// let document = BsonDocument([BsonElement("_id", id)]) +// let collection = db.GetCollection "ObjectWithOptions" +// collection.InsertOne document +// +// let collection = db.GetCollection "ObjectWithOptions" +// let fromDb = collection.Find(fun x -> x.Id = id).ToList().First() +// fromDb.Age |> should equal None +// +// [] +// let ``We can integrate serialize & deserialize on DimmerSwitches``() = +// let collection = db.GetCollection "ObjectWithDimmers" +// let obj = ObjectWithDimmers(Kitchen = Off, +// Bedroom1 = Dim 42, +// Bedroom2 = DimMarquee(12, "when I was little...")) +// collection.InsertOne obj +// +// let fromDb = collection.Find(fun x -> x.Id = obj.Id).ToList().First() +// match fromDb.Kitchen with +// | Off -> () +// | _ -> failwith "Kitchen light wasn't off" +// +// match fromDb.Bedroom1 with +// | Dim 42 -> () +// | _ -> failwith "Bedroom1 light wasn't dim enough" +// +// match fromDb.Bedroom2 with +// | DimMarquee(12, "when I was little...") -> () +// | _ -> failwith "Bedroom2 doesn't have the party we thought" +// +// [] +// let ``It can serialize record with list`` () = +// let collection = db.GetCollection "RecordWithCollections" +// let obj = +// { Id = newBsonObjectId() +// IntVal = 123 +// DoubleVal = 1.23 +// ListVal = [1; 2; 3] +// IntValOpt = ValueSome 42 +// SetVal = ["toto"; "titi"; "tata"] |> Set |> Some +// MapVal = ["toto", 42; "titi", 666] |> Map |> Some +// OptionVal = Some 123 } +// collection.InsertOne obj +// +// let testCollection = db.GetCollection "RecordWithCollections" +// Console.WriteLine((testCollection |> findById obj.Id).ToJson()) +// +// let fromDb = collection.Find(fun x -> x.Id = obj.Id).ToList().First() +// fromDb |> should equal obj diff --git a/FSharp.MongoDB.Driver.Tests/FSharp.MongoDB.Driver.Tests.fsproj b/FSharp.MongoDB.Driver.Tests/FSharp.MongoDB.Driver.Tests.fsproj index 8dc36c0..4457adb 100644 --- a/FSharp.MongoDB.Driver.Tests/FSharp.MongoDB.Driver.Tests.fsproj +++ b/FSharp.MongoDB.Driver.Tests/FSharp.MongoDB.Driver.Tests.fsproj @@ -8,6 +8,7 @@ + diff --git a/FSharp.MongoDB.Driver.Tests/Serializers.fs b/FSharp.MongoDB.Driver.Tests/Serializers.fs new file mode 100644 index 0000000..7485b81 --- /dev/null +++ b/FSharp.MongoDB.Driver.Tests/Serializers.fs @@ -0,0 +1,112 @@ +module FSharp.MongoDB.Driver.Serializers.Tests +open FsUnit +open NUnit.Framework +open MongoDB.Bson +open MongoDB.Driver + +let mutable client: MongoClient = Unchecked.defaultof +let mutable db: IMongoDatabase = Unchecked.defaultof + +type TheRecord = + { A: int + B: string option } + +[] +type Value = + | None + | Int of a:int * b:int + | Float of float + | String of string + | TheRecord of toto:TheRecord + +type Class() = + member val Id : ObjectId = ObjectId.GenerateNewId() with get, set + member val List : int list = [] with get, set + + +[] +type CollectionItem = + { Id: ObjectId + Int: int + IntOption: int option + + String: string + StringOption: string option + + Record: TheRecord + // RecordOption: TheRecord option + + List: int list + ListOption: int list option + RecordListOption: TheRecord list option + + Set: Set + Map: Map + MapOption: Map option + + DU: Value + DUList: Value list } + + +[] +let init() = + let connectionString = "mongodb://localhost" + let dbname = "FSharp-MongoDB-Driver" + client <- new MongoClient(connectionString) + client.DropDatabase(dbname) + db <- client.GetDatabase(dbname) + FSharp.MongoDB.Driver.Register() + +[] +let teardown() = + client.Dispose() + +[] +let ``Roundtrip complex record with Some``() = + let obj = + { Id = ObjectId.GenerateNewId() + Int = 42 + IntOption = Some 666 + String = "toto" + StringOption = Some "tata" + Record = { A = 42; B = Some "titi" } + // RecordOption = Some { A = 666; B = Some "tutu" } + List = [ 1; 2; 3 ] + ListOption = Some [ 4; 5; 6; 7 ] + RecordListOption = Some [ { A = 42; B = Some "titi" }; { A = 666; B = Some "tutu" } ] + Set = Set [ "1"; "2"; "3" ] + Map = Map [ "tata", 1; "titi", 2; "tutu", 3 ] + MapOption = Some <| Map ["toto", 42 ] + DU = Value.Int (42, 666) // TheRecord { A = 1; B = Some "tata" } // // + DUList = [ Value.Int (42, 33); Value.Float 1.23 ] } + + let collection = db.GetCollection "CollectionItem" + collection.InsertOne(obj) + + let fromDb = collection.Find(fun x -> x.Id = obj.Id).First() + fromDb |> should equal obj + +[] +let ``Roundtrip complex record with None``() = + let obj = + { Id = ObjectId.GenerateNewId() + Int = 42 + IntOption = None + String = "toto" + StringOption = None + Record = { A = 42; B = Some "titi" } + // RecordOption = Some { A = 666; B = Some "tutu" } + List = [ 1; 2; 3 ] + ListOption = None + RecordListOption = None + Set = Set [ "1"; "2"; "3" ] + Map = Map [ "tata", 1; "titi", 2; "tutu", 3 ] + MapOption = None + DU = Value.Int (42, 666) // TheRecord { A = 1; B = Some "tata" } // // + DUList = [ Value.Int (42, 33); Value.Float 1.23 ] } + + let collection = db.GetCollection "CollectionItem" + collection.InsertOne(obj) + + let fromDb = collection.Find(fun x -> x.Id = obj.Id).First() + fromDb |> should equal obj diff --git a/FSharp.MongoDB.Driver/Helpers.fs b/FSharp.MongoDB.Driver/Helpers.fs index 2d39f2d..e448d5f 100644 --- a/FSharp.MongoDB.Driver/Helpers.fs +++ b/FSharp.MongoDB.Driver/Helpers.fs @@ -1,5 +1,6 @@ module FSharp.Helpers open System +open MongoDB.Bson.Serialization let fsharpType (typ : Type) = typ.GetCustomAttributes(typeof, true) diff --git a/FSharp.MongoDB.Driver/Serializers/List.fs b/FSharp.MongoDB.Driver/Serializers/List.fs index 19250c3..b05f567 100644 --- a/FSharp.MongoDB.Driver/Serializers/List.fs +++ b/FSharp.MongoDB.Driver/Serializers/List.fs @@ -5,11 +5,12 @@ open MongoDB.Bson.Serialization type internal ListSerializer<'T>() = inherit SerializerBase>() - let contentSerializer = BsonSerializer.LookupSerializer(typeof>) + let contentSerializer = BsonSerializer.LookupSerializer(typeof>) override _.Serialize(context, _, value) = - contentSerializer.Serialize(context, value) + let list = value |> System.Collections.Generic.List<'T> + contentSerializer.Serialize(context, list) override _.Deserialize(context, args) = - let list = contentSerializer.Deserialize(context, args) :?> System.Collections.Generic.IEnumerable<'T> + let list = contentSerializer.Deserialize(context, args) :?> System.Collections.Generic.IList<'T> list |> List.ofSeq diff --git a/FSharp.MongoDB.Driver/Serializers/UnionCase.fs b/FSharp.MongoDB.Driver/Serializers/UnionCase.fs index 1d8613d..44f7ffc 100644 --- a/FSharp.MongoDB.Driver/Serializers/UnionCase.fs +++ b/FSharp.MongoDB.Driver/Serializers/UnionCase.fs @@ -11,39 +11,32 @@ open Microsoft.FSharp.Reflection type internal UnionCaseSerializer<'T>() = inherit SerializerBase<'T>() - let readItems context args (types : Type seq) = - types - |> Seq.fold(fun state t -> - let serializer = BsonSerializer.LookupSerializer(t) - let item = serializer.Deserialize(context, args) - item :: state) [] - |> Seq.toArray |> Array.rev - override _.Serialize(context, args, value) = + let info, values = FSharpValue.GetUnionFields(value, args.NominalType) let writer = context.Writer writer.WriteStartDocument() - let info, values = FSharpValue.GetUnionFields(value, args.NominalType) - writer.WriteName(info.Name) - writer.WriteStartArray() - values - |> Seq.zip(info.GetFields()) + writer.WriteString("_t", info.Name) + values + |> Seq.zip (info.GetFields()) |> Seq.iter (fun (field, value) -> let itemSerializer = BsonSerializer.LookupSerializer(field.PropertyType) + writer.WriteName(field.Name) itemSerializer.Serialize(context, args, value)) - writer.WriteEndArray() writer.WriteEndDocument() override _.Deserialize(context, args) = let reader = context.Reader reader.ReadStartDocument() - let typeName = reader.ReadName() - let unionType = + let typeName = reader.ReadString("_t") + let unionType = FSharpType.GetUnionCases(args.NominalType) - |> Seq.where (fun case -> case.Name = typeName) - |> Seq.head - reader.ReadStartArray() - let items = readItems context args (unionType.GetFields() |> Seq.map(fun f -> f.PropertyType)) - reader.ReadEndArray() + |> Seq.find (fun case -> case.Name = typeName) + let items = + unionType.GetFields() + |> Array.map (fun prop -> + let serializer = BsonSerializer.LookupSerializer(prop.PropertyType) + reader.ReadName(prop.Name) + let item = serializer.Deserialize(context, args) + item) reader.ReadEndDocument() FSharpValue.MakeUnion(unionType, items) :?> 'T - diff --git a/README.md b/README.md index 90f11b3..020615d 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,13 @@ -[![Build status](https://github.com/pchalamet/FSharp.MongoDB.Driver/workflows/build/badge.svg)](https://github.com/pchalamet/FSharp.MongoDB.Driver/actions?query=workflow%3Abuild) +![Build status](https://github.com/pchalamet/FSharp.MongoDB.Driver/actions/workflows/build.yml/badge.svg?branch=main) + +# WARNING +:exclamation: This is alpha quality. Following cases do not work as of now: +* DU of record # FSharp.MongoDB.Driver This project adds support for F# types to the [official .NET MongoDB driver][1]. -It's a fork of [MongoDB.FSharp](https://github.com/tkellogg/MongoDB.FSharp) and has been extensively reworked to make it support .net 9 and nullable. +It's a fork of [MongoDB.FSharp](https://github.com/tkellogg/MongoDB.FSharp) and has been extensively reworked to support .net 9 and other features. Following types are supported: * List @@ -13,13 +17,8 @@ Following types are supported: * ValueOption * Discriminated Unions -Records are supported as well out of the box with official MongoDB driver. Probably you want to add `CLIMutable` attribute on the record to support automatic ObjectId initialization. -``` -[] -type RecordTypeOptId = - { [] Id : ObjectId - Name : string } -``` +## Breaking changes vs MongoDB.FSharp +* Discriminated unions are serialized with more information as this now uses DU property names. # Installation Install this project via NuGet. @@ -61,6 +60,15 @@ Otherwise the value. ## Map key/value mapping. -# Union Case +## Discriminated Unions key is the case name.\ value is an array of the values of the case + +## Record +Records are supported as well out of the box with official MongoDB driver. Probably you want to add `CLIMutable` attribute on the record to support upsert operations. +``` +[] +type RecordTypeOptId = + { [] Id : ObjectId + Name : string } +```