From 750efc62d298cf89e2080893a7e444b433e746e4 Mon Sep 17 00:00:00 2001 From: rstam Date: Sat, 30 Oct 2010 15:37:27 -0400 Subject: [PATCH] Implemented NullableTypeSerializer. --- Bson/Bson.csproj | 1 + .../BsonDefaultSerializationProvider.cs | 7 + .../Serializers/NullableTypeSerializer.cs | 74 ++++++++ BsonUnitTests/BsonUnitTests.csproj | 1 + .../NullableTypeSerializerTests.cs | 164 ++++++++++++++++++ 5 files changed, 247 insertions(+) create mode 100644 Bson/DefaultSerializer/Serializers/NullableTypeSerializer.cs create mode 100644 BsonUnitTests/DefaultSerializer/Serializers/NullableTypeSerializerTests.cs diff --git a/Bson/Bson.csproj b/Bson/Bson.csproj index 24ceb35cbf..0c0e00823c 100644 --- a/Bson/Bson.csproj +++ b/Bson/Bson.csproj @@ -86,6 +86,7 @@ + diff --git a/Bson/DefaultSerializer/BsonDefaultSerializationProvider.cs b/Bson/DefaultSerializer/BsonDefaultSerializationProvider.cs index 57f072dc2a..efdf5c5420 100644 --- a/Bson/DefaultSerializer/BsonDefaultSerializationProvider.cs +++ b/Bson/DefaultSerializer/BsonDefaultSerializationProvider.cs @@ -88,6 +88,13 @@ namespace MongoDB.Bson.DefaultSerializer { return GeneralEnumSerializer.GetSerializer(serializationOptions); } + if ( + type.IsGenericType && + type.GetGenericTypeDefinition() == typeof(Nullable<>) + ) { + return NullableTypeSerializer.Singleton; + } + if ( type.IsClass && !typeof(Array).IsAssignableFrom(type) && diff --git a/Bson/DefaultSerializer/Serializers/NullableTypeSerializer.cs b/Bson/DefaultSerializer/Serializers/NullableTypeSerializer.cs new file mode 100644 index 0000000000..27e18e3a97 --- /dev/null +++ b/Bson/DefaultSerializer/Serializers/NullableTypeSerializer.cs @@ -0,0 +1,74 @@ +/* Copyright 2010 10gen Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +using MongoDB.Bson.IO; +using MongoDB.Bson.Serialization; + +namespace MongoDB.Bson.DefaultSerializer { + public class NullableTypeSerializer : BsonBaseSerializer { + #region private static fields + private static NullableTypeSerializer singleton = new NullableTypeSerializer(); + #endregion + + #region constructors + private NullableTypeSerializer() { + } + #endregion + + #region public static properties + public static NullableTypeSerializer Singleton { + get { return singleton; } + } + #endregion + + #region public methods + public override object DeserializeElement( + BsonReader bsonReader, + Type nominalType, + out string name + ) { + var bsonType = bsonReader.PeekBsonType(); + if (bsonType == BsonType.Null) { + bsonReader.ReadNull(out name); + return null; + } else { + Type underlyingType = Nullable.GetUnderlyingType(nominalType); + return BsonSerializer.DeserializeElement(bsonReader, underlyingType, out name); + } + } + + public override void SerializeElement( + BsonWriter bsonWriter, + Type nominalType, + string name, + object value + ) { + if (value == null) { + bsonWriter.WriteNull(name); + } else { + Type underlyingType = Nullable.GetUnderlyingType(nominalType); + BsonSerializer.SerializeElement(bsonWriter, underlyingType, name, value); + } + } + #endregion + } +} diff --git a/BsonUnitTests/BsonUnitTests.csproj b/BsonUnitTests/BsonUnitTests.csproj index c20ad65c16..334929f7cf 100644 --- a/BsonUnitTests/BsonUnitTests.csproj +++ b/BsonUnitTests/BsonUnitTests.csproj @@ -81,6 +81,7 @@ + diff --git a/BsonUnitTests/DefaultSerializer/Serializers/NullableTypeSerializerTests.cs b/BsonUnitTests/DefaultSerializer/Serializers/NullableTypeSerializerTests.cs new file mode 100644 index 0000000000..2c0b514d47 --- /dev/null +++ b/BsonUnitTests/DefaultSerializer/Serializers/NullableTypeSerializerTests.cs @@ -0,0 +1,164 @@ +/* Copyright 2010 10gen Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using NUnit.Framework; + +using MongoDB.Bson; +using MongoDB.Bson.DefaultSerializer; +using MongoDB.Bson.IO; +using MongoDB.Bson.Serialization; + +namespace MongoDB.BsonUnitTests.DefaultSerializer { + [TestFixture] + public class NullableTypeSerializerTests { + private class C { + public bool? Boolean { get; set; } + public DateTime? DateTime { get; set; } + [BsonDateTimeOptions(DateOnly = true)] + public DateTime? DateOnly { get; set; } + public double? Double { get; set; } + public Guid? Guid { get; set; } + public int? Int32 { get; set; } + public long? Int64 { get; set; } + public ObjectId? ObjectId { get; set; } + + } + + private const string template = + "{ " + + "'Boolean' : null, " + + "'DateTime' : null, " + + "'DateOnly' : null, " + + "'Double' : null, " + + "'Guid' : null, " + + "'Int32' : null, " + + "'Int64' : null, " + + "'ObjectId' : null" + + " }"; + + [Test] + public void TestAllNulls() { + C c = new C(); + var json = c.ToJson(); + var expected = template.Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = c.ToBson(); + var rehydrated = BsonSerializer.DeserializeDocument(bson); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + [Test] + public void TestBoolean() { + C c = new C { Boolean = true }; + var json = c.ToJson(); + var expected = template.Replace("'Boolean' : null", "'Boolean' : true").Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = c.ToBson(); + var rehydrated = BsonSerializer.DeserializeDocument(bson); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + [Test] + public void TestDateTime() { + C c = new C { DateTime = BsonConstants.UnixEpoch }; + var json = c.ToJson(); + var expected = template.Replace("'DateTime' : null", "'DateTime' : { '$date' : 0 }").Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = c.ToBson(); + var rehydrated = BsonSerializer.DeserializeDocument(bson); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + [Test] + public void TestDateOnly() { + C c = new C { DateOnly = BsonConstants.UnixEpoch }; + var json = c.ToJson(); + var expected = template.Replace("'DateOnly' : null", "'DateOnly' : { '$date' : 0 }").Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = c.ToBson(); + var rehydrated = BsonSerializer.DeserializeDocument(bson); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + [Test] + public void TestDouble() { + C c = new C { Double = 1.5 }; + var json = c.ToJson(); + var expected = template.Replace("'Double' : null", "'Double' : 1.5").Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = c.ToBson(); + var rehydrated = BsonSerializer.DeserializeDocument(bson); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + [Test] + public void TestGuid() { + C c = new C { Guid = Guid.Empty }; + var json = c.ToJson(); + var expected = template.Replace("'Guid' : null", "'Guid' : { '$binary' : 'AAAAAAAAAAAAAAAAAAAAAA==', '$type' : '03' }").Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = c.ToBson(); + var rehydrated = BsonSerializer.DeserializeDocument(bson); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + [Test] + public void TestInt32() { + C c = new C { Int32 = 1 }; + var json = c.ToJson(); + var expected = template.Replace("'Int32' : null", "'Int32' : 1").Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = c.ToBson(); + var rehydrated = BsonSerializer.DeserializeDocument(bson); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + [Test] + public void TestInt64() { + C c = new C { Int64 = 2 }; + var json = c.ToJson(); + var expected = template.Replace("'Int64' : null", "'Int64' : 2").Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = c.ToBson(); + var rehydrated = BsonSerializer.DeserializeDocument(bson); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + [Test] + public void TestObjectId() { + C c = new C { ObjectId = ObjectId.Empty }; + var json = c.ToJson(); + var expected = template.Replace("'ObjectId' : null", "'ObjectId' : { '$oid' : '000000000000000000000000' }").Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = c.ToBson(); + var rehydrated = BsonSerializer.DeserializeDocument(bson); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + } +}