diff --git a/Bson/Bson.csproj b/Bson/Bson.csproj index c5b607aea3..81a124810b 100644 --- a/Bson/Bson.csproj +++ b/Bson/Bson.csproj @@ -87,8 +87,9 @@ - + + diff --git a/Bson/DefaultSerializer/Serializers/CollectionSerializersGeneric.cs b/Bson/DefaultSerializer/Serializers/CollectionGenericSerializers.cs similarity index 99% rename from Bson/DefaultSerializer/Serializers/CollectionSerializersGeneric.cs rename to Bson/DefaultSerializer/Serializers/CollectionGenericSerializers.cs index 37a5dc6b58..2ab9d77d2d 100644 --- a/Bson/DefaultSerializer/Serializers/CollectionSerializersGeneric.cs +++ b/Bson/DefaultSerializer/Serializers/CollectionGenericSerializers.cs @@ -39,6 +39,9 @@ namespace MongoDB.Bson.DefaultSerializer { public class EnumerableSerializer : BsonBaseSerializer { #region constructors + public EnumerableSerializer() { + } + public EnumerableSerializer( object serializationOptions ) { diff --git a/Bson/DefaultSerializer/Serializers/DictionaryGenericSerializer.cs b/Bson/DefaultSerializer/Serializers/DictionaryGenericSerializer.cs new file mode 100644 index 0000000000..9da36317cc --- /dev/null +++ b/Bson/DefaultSerializer/Serializers/DictionaryGenericSerializer.cs @@ -0,0 +1,158 @@ +/* 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.Linq; +using System.Text; +using System.IO; + +using MongoDB.Bson.IO; +using MongoDB.Bson.Serialization; + +namespace MongoDB.Bson.DefaultSerializer { + public static class DictionarySerializerRegistration { + #region public static methods + public static void RegisterGenericSerializerDefinitions() { + BsonSerializer.RegisterGenericSerializerDefinition(typeof(Dictionary<,>), typeof(DictionarySerializer<,>)); + BsonSerializer.RegisterGenericSerializerDefinition(typeof(IDictionary<,>), typeof(DictionarySerializer<,>)); + BsonSerializer.RegisterGenericSerializerDefinition(typeof(SortedDictionary<,>), typeof(DictionarySerializer<,>)); + BsonSerializer.RegisterGenericSerializerDefinition(typeof(SortedList<,>), typeof(DictionarySerializer<,>)); + } + #endregion + } + + public class DictionarySerializer : BsonBaseSerializer { + #region constructors + public DictionarySerializer() { + } + + public DictionarySerializer( + object serializationOptions + ) { + } + #endregion + + #region public methods + public override object Deserialize( + BsonReader bsonReader, + Type nominalType + ) { + var bsonType = bsonReader.CurrentBsonType; + if (bsonType == BsonType.Null) { + bsonReader.ReadNull(); + return null; + } else if (bsonType == BsonType.Document) { + var dictionary = CreateInstance(nominalType); + bsonReader.ReadStartDocument(); + var valueDiscriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(typeof(TValue)); + while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) { + var key = (TKey) (object) bsonReader.ReadName(); + var valueType = valueDiscriminatorConvention.GetActualType(bsonReader, typeof(TValue)); + var valueSerializer = BsonSerializer.LookupSerializer(valueType); + var value = (TValue) valueSerializer.Deserialize(bsonReader, typeof(TValue)); + dictionary.Add(key, value); + } + bsonReader.ReadEndDocument(); + return dictionary; + } else if (bsonType == BsonType.Array) { + var dictionary = CreateInstance(nominalType); + bsonReader.ReadStartArray(); + var keyDiscriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(typeof(TKey)); + var valueDiscriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(typeof(TValue)); + while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) { + bsonReader.SkipName(); + bsonReader.ReadStartArray(); + bsonReader.ReadBsonType(); + bsonReader.SkipName(); + var keyType = keyDiscriminatorConvention.GetActualType(bsonReader, typeof(TKey)); + var keySerializer = BsonSerializer.LookupSerializer(keyType); + var key = (TKey) keySerializer.Deserialize(bsonReader, typeof(TKey)); + bsonReader.ReadBsonType(); + bsonReader.SkipName(); + var valueType = valueDiscriminatorConvention.GetActualType(bsonReader, typeof(TValue)); + var valueSerializer = BsonSerializer.LookupSerializer(valueType); + var value = (TValue) valueSerializer.Deserialize(bsonReader, typeof(TValue)); + bsonReader.ReadEndArray(); + dictionary.Add(key, value); + } + bsonReader.ReadEndArray(); + return dictionary; + } else { + var message = string.Format("Can't deserialize a {0} from BsonType {1}", nominalType.FullName, bsonType); + throw new FileFormatException(message); + } + } + + public override void Serialize( + BsonWriter bsonWriter, + Type nominalType, + object value, + bool serializeIdFirst + ) { + if (value == null) { + bsonWriter.WriteNull(); + } else { + var dictionary = (IDictionary) value; + if ( + typeof(TKey) == typeof(string) || + (typeof(TKey) == typeof(object) && dictionary.Keys.All(o => o.GetType() == typeof(string))) + ) { + bsonWriter.WriteStartDocument(); + int index = 0; + foreach (KeyValuePair entry in dictionary) { + bsonWriter.WriteName((string) (object) entry.Key); + BsonSerializer.Serialize(bsonWriter, typeof(TValue), entry.Value); + index++; + } + bsonWriter.WriteEndDocument(); + } else { + bsonWriter.WriteStartArray(); + int index = 0; + foreach (KeyValuePair entry in dictionary) { + bsonWriter.WriteStartArray(index.ToString()); + bsonWriter.WriteName("0"); + BsonSerializer.Serialize(bsonWriter, typeof(object), entry.Key); + bsonWriter.WriteName("1"); + BsonSerializer.Serialize(bsonWriter, typeof(object), entry.Value); + bsonWriter.WriteEndArray(); + index++; + } + bsonWriter.WriteEndArray(); + } + } + } + #endregion + + #region private methods + private IDictionary CreateInstance( + Type nominalType + ) { + if (nominalType == typeof(Dictionary)) { + return new Dictionary(); + } else if (nominalType == typeof(IDictionary)) { + return new Dictionary(); + } else if (nominalType == typeof(SortedDictionary)) { + return new SortedDictionary(); + } else if (nominalType == typeof(SortedList)) { + return new SortedList(); + } else { + var message = string.Format("Invalid nominalType for DictionarySerializer<{0}, {1}>: {2}", typeof(TKey).FullName, typeof(TValue).FullName, nominalType.FullName); + throw new BsonSerializationException(message); + } + } + #endregion + } +} diff --git a/Bson/DefaultSerializer/Serializers/DictionarySerializer.cs b/Bson/DefaultSerializer/Serializers/DictionarySerializer.cs index 8da84bfdcf..a2c26b04f2 100644 --- a/Bson/DefaultSerializer/Serializers/DictionarySerializer.cs +++ b/Bson/DefaultSerializer/Serializers/DictionarySerializer.cs @@ -59,6 +59,19 @@ namespace MongoDB.Bson.DefaultSerializer { if (bsonType == BsonType.Null) { bsonReader.ReadNull(); return null; + } else if (bsonType == BsonType.Document) { + var dictionary = CreateInstance(nominalType); + bsonReader.ReadStartDocument(); + var discriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(typeof(object)); + while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) { + var key = bsonReader.ReadName(); + var valueType = discriminatorConvention.GetActualType(bsonReader, typeof(object)); + var valueSerializer = BsonSerializer.LookupSerializer(valueType); + var value = valueSerializer.Deserialize(bsonReader, typeof(object)); + dictionary.Add(key, value); + } + bsonReader.ReadEndDocument(); + return dictionary; } else if (bsonType == BsonType.Array) { var dictionary = CreateInstance(nominalType); bsonReader.ReadStartArray(); @@ -81,19 +94,6 @@ namespace MongoDB.Bson.DefaultSerializer { } bsonReader.ReadEndArray(); return dictionary; - } else if (bsonType == BsonType.Document) { - var dictionary = CreateInstance(nominalType); - bsonReader.ReadStartDocument(); - var discriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(typeof(object)); - while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) { - var key = bsonReader.ReadName(); - var valueType = discriminatorConvention.GetActualType(bsonReader, typeof(object)); - var valueSerializer = BsonSerializer.LookupSerializer(valueType); - var value = valueSerializer.Deserialize(bsonReader, typeof(object)); - dictionary.Add(key, value); - } - bsonReader.ReadEndDocument(); - return dictionary; } else { var message = string.Format("Can't deserialize a {0} from BsonType {1}", nominalType.FullName, bsonType); throw new FileFormatException(message); @@ -110,7 +110,16 @@ namespace MongoDB.Bson.DefaultSerializer { bsonWriter.WriteNull(); } else { var dictionary = (IDictionary) value; - if (dictionary.Keys.Cast().Any(o => o.GetType() != typeof(string))) { + if (dictionary.Keys.Cast().All(o => o.GetType() == typeof(string))) { + bsonWriter.WriteStartDocument(); + int index = 0; + foreach (DictionaryEntry entry in dictionary) { + bsonWriter.WriteName((string) entry.Key); + BsonSerializer.Serialize(bsonWriter, typeof(object), entry.Value); + index++; + } + bsonWriter.WriteEndDocument(); + } else { bsonWriter.WriteStartArray(); int index = 0; foreach (DictionaryEntry entry in dictionary) { @@ -123,16 +132,7 @@ namespace MongoDB.Bson.DefaultSerializer { index++; } bsonWriter.WriteEndArray(); - } else { - bsonWriter.WriteStartDocument(); - int index = 0; - foreach (DictionaryEntry entry in dictionary) { - bsonWriter.WriteName((string) entry.Key); - BsonSerializer.Serialize(bsonWriter, typeof(object), entry.Value); - index++; - } - bsonWriter.WriteEndDocument(); - } + } } } #endregion diff --git a/BsonUnitTests/BsonUnitTests.csproj b/BsonUnitTests/BsonUnitTests.csproj index a1e36c101f..da3b130004 100644 --- a/BsonUnitTests/BsonUnitTests.csproj +++ b/BsonUnitTests/BsonUnitTests.csproj @@ -85,6 +85,7 @@ + diff --git a/BsonUnitTests/DefaultSerializer/Serializers/DictionaryGenericSerializerTests.cs b/BsonUnitTests/DefaultSerializer/Serializers/DictionaryGenericSerializerTests.cs new file mode 100644 index 0000000000..5a5d7a9a07 --- /dev/null +++ b/BsonUnitTests/DefaultSerializer/Serializers/DictionaryGenericSerializerTests.cs @@ -0,0 +1,546 @@ +/* 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.Linq; +using System.Text; +using NUnit.Framework; + +using MongoDB.Bson; +using MongoDB.Bson.DefaultSerializer; +using MongoDB.Bson.Serialization; + +namespace MongoDB.BsonUnitTests.DefaultSerializer.DictionaryGenericSerializers { + [BsonDiscriminator("DictionaryGenericSerializers.C")] // "C" is an ambiguous discriminator when nominalType is System.Object + public class C { + public string P { get; set; } + } + + [TestFixture] + public class DictionaryGenericSerializerTests { + public class T { + public Dictionary D { get; set; } + public IDictionary ID { get; set; } + public SortedDictionary SD { get; set; } + public SortedList SL { get; set; } + } + + [Test] + public void TestNull() { + var obj = new T { D = null, ID = null, SD = null, SL = null }; + var json = obj.ToJson(); + var rep = "null"; + var expected = "{ 'D' : #R, 'ID' : #R, 'SD' : #R, 'SL' : #R }".Replace("#R", rep).Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = obj.ToBson(); + var rehydrated = BsonSerializer.Deserialize(bson); + Assert.IsNull(rehydrated.D); + Assert.IsNull(rehydrated.ID); + Assert.IsNull(rehydrated.SD); + Assert.IsNull(rehydrated.SL); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + [Test] + public void TestEmpty() { + var d = new Dictionary(); + var sd = CreateSortedDictionary(d); + var sl = CreateSortedList(d); + var obj = new T { D = d, ID = d, SD = sd, SL = sl }; + var json = obj.ToJson(); + var rep = "{ }"; + var expected = "{ 'D' : #R, 'ID' : #R, 'SD' : #R, 'SL' : #R }".Replace("#R", rep).Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = obj.ToBson(); + var rehydrated = BsonSerializer.Deserialize(bson); + Assert.IsInstanceOf>(rehydrated.D); + Assert.IsInstanceOf>(rehydrated.ID); + Assert.IsInstanceOf>(rehydrated.SD); + Assert.IsInstanceOf>(rehydrated.SL); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + [Test] + public void TestOneC() { + var d = new Dictionary { { "A", new C { P = "x" } } }; + var sd = CreateSortedDictionary(d); + var sl = CreateSortedList(d); + var obj = new T { D = d, ID = d, SD = sd, SL = sl }; + var json = obj.ToJson(); + var rep = "{ 'A' : { '_t' : 'DictionaryGenericSerializers.C', 'P' : 'x' } }"; + var expected = "{ 'D' : #R, 'ID' : #R, 'SD' : #R, 'SL' : #R }".Replace("#R", rep).Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = obj.ToBson(); + var rehydrated = BsonSerializer.Deserialize(bson); + Assert.IsInstanceOf>(rehydrated.D); + Assert.IsInstanceOf>(rehydrated.ID); + Assert.IsInstanceOf>(rehydrated.SD); + Assert.IsInstanceOf>(rehydrated.SL); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + [Test] + public void TestOneInt() { + var d = new Dictionary { { "A", 1 } }; + var sd = CreateSortedDictionary(d); + var sl = CreateSortedList(d); + var obj = new T { D = d, ID = d, SD = sd, SL = sl }; + var json = obj.ToJson(); + var rep = "{ 'A' : 1 }"; + var expected = "{ 'D' : #R, 'ID' : #R, 'SD' : #R, 'SL' : #R }".Replace("#R", rep).Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = obj.ToBson(); + var rehydrated = BsonSerializer.Deserialize(bson); + Assert.IsInstanceOf>(rehydrated.D); + Assert.IsInstanceOf>(rehydrated.ID); + Assert.IsInstanceOf>(rehydrated.SD); + Assert.IsInstanceOf>(rehydrated.SL); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + [Test] + public void TestOneIntWithIntKey() { + var d = new Dictionary { { 1, 2 } }; + var sd = CreateSortedDictionary(d); + var sl = CreateSortedList(d); + var obj = new T { D = d, ID = d, SD = sd, SL = sl }; + var json = obj.ToJson(); + var rep = "[[1, 2]]"; + var expected = "{ 'D' : #R, 'ID' : #R, 'SD' : #R, 'SL' : #R }".Replace("#R", rep).Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = obj.ToBson(); + var rehydrated = BsonSerializer.Deserialize(bson); + Assert.IsInstanceOf>(rehydrated.D); + Assert.IsInstanceOf>(rehydrated.ID); + Assert.IsInstanceOf>(rehydrated.SD); + Assert.IsInstanceOf>(rehydrated.SL); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + [Test] + public void TestOneString() { + var d = new Dictionary { { "A", "x" } }; + var sd = CreateSortedDictionary(d); + var sl = CreateSortedList(d); + var obj = new T { D = d, ID = d, SD = sd, SL = sl }; + var json = obj.ToJson(); + var rep = "{ 'A' : 'x' }"; + var expected = "{ 'D' : #R, 'ID' : #R, 'SD' : #R, 'SL' : #R }".Replace("#R", rep).Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = obj.ToBson(); + var rehydrated = BsonSerializer.Deserialize(bson); + Assert.IsInstanceOf>(rehydrated.D); + Assert.IsInstanceOf>(rehydrated.ID); + Assert.IsInstanceOf>(rehydrated.SD); + Assert.IsInstanceOf>(rehydrated.SL); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + [Test] + public void TestOneStringWithIntKey() { + var d = new Dictionary { { 1, "x" } }; + var sd = CreateSortedDictionary(d); + var sl = CreateSortedList(d); + var obj = new T { D = d, ID = d, SD = sd, SL = sl }; + var json = obj.ToJson(); + var rep = "[[1, 'x']]"; + var expected = "{ 'D' : #R, 'ID' : #R, 'SD' : #R, 'SL' : #R }".Replace("#R", rep).Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = obj.ToBson(); + var rehydrated = BsonSerializer.Deserialize(bson); + Assert.IsInstanceOf>(rehydrated.D); + Assert.IsInstanceOf>(rehydrated.ID); + Assert.IsInstanceOf>(rehydrated.SD); + Assert.IsInstanceOf>(rehydrated.SL); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + [Test] + public void TestTwoCs() { + var d = new Dictionary { { "A", new C { P = "x" } }, { "B", new C { P = "y" } } }; + var sd = CreateSortedDictionary(d); + var sl = CreateSortedList(d); + var obj = new T { D = d, ID = d, SD = sd, SL = sl }; + var json = obj.ToJson(); + var reps = new Dictionary { + { "A", "{ '_t' : 'DictionaryGenericSerializers.C', 'P' : 'x' }"}, + { "B", "{ '_t' : 'DictionaryGenericSerializers.C', 'P' : 'y' }"} + }; + var htRep = GetDocumentRepresentationInKeyOrder(d, reps); + var sdRep = GetDocumentRepresentationInKeyOrder(sd, reps); + var slRep = GetDocumentRepresentationInKeyOrder(sl, reps); + var expected = "{ 'D' : #D, 'ID' : #D, 'SD' : #SD, 'SL' : #SL }"; + expected = expected.Replace("#D", htRep); + expected = expected.Replace("#SD", sdRep); + expected = expected.Replace("#SL", slRep); + expected = expected.Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = obj.ToBson(); + var rehydrated = BsonSerializer.Deserialize(bson); + Assert.IsInstanceOf>(rehydrated.D); + Assert.IsInstanceOf>(rehydrated.ID); + Assert.IsInstanceOf>(rehydrated.SD); + Assert.IsInstanceOf>(rehydrated.SL); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + [Test] + public void TestTwoCsWithIntKeys() { + var d = new Dictionary { { 1, new C { P = "x" } }, { 2, new C { P = "y" } } }; + var sd = CreateSortedDictionary(d); + var sl = CreateSortedList(d); + var obj = new T { D = d, ID = d, SD = sd, SL = sl }; + var json = obj.ToJson(); + var reps = new Dictionary { + { 1, "[1, { '_t' : 'DictionaryGenericSerializers.C', 'P' : 'x' }]"}, + { 2, "[2, { '_t' : 'DictionaryGenericSerializers.C', 'P' : 'y' }]"} + }; + var htRep = GetArrayRepresentationInKeyOrder(d, reps); + var sdRep = GetArrayRepresentationInKeyOrder(sd, reps); + var slRep = GetArrayRepresentationInKeyOrder(sl, reps); + var expected = "{ 'D' : #D, 'ID' : #D, 'SD' : #SD, 'SL' : #SL }"; + expected = expected.Replace("#D", htRep); + expected = expected.Replace("#SD", sdRep); + expected = expected.Replace("#SL", slRep); + expected = expected.Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = obj.ToBson(); + var rehydrated = BsonSerializer.Deserialize(bson); + Assert.IsInstanceOf>(rehydrated.D); + Assert.IsInstanceOf>(rehydrated.ID); + Assert.IsInstanceOf>(rehydrated.SD); + Assert.IsInstanceOf>(rehydrated.SL); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + [Test] + public void TestTwoInts() { + var d = new Dictionary { { "A", 1 }, { "B", 2 } }; + var sd = CreateSortedDictionary(d); + var sl = CreateSortedList(d); + var obj = new T { D = d, ID = d, SD = sd, SL = sl }; + var json = obj.ToJson(); + var reps = new Dictionary { + { "A", "1"}, + { "B", "2"} + }; + var htRep = GetDocumentRepresentationInKeyOrder(d, reps); + var sdRep = GetDocumentRepresentationInKeyOrder(sd, reps); + var slRep = GetDocumentRepresentationInKeyOrder(sl, reps); + var expected = "{ 'D' : #D, 'ID' : #D, 'SD' : #SD, 'SL' : #SL }"; + expected = expected.Replace("#D", htRep); + expected = expected.Replace("#SD", sdRep); + expected = expected.Replace("#SL", slRep); + expected = expected.Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = obj.ToBson(); + var rehydrated = BsonSerializer.Deserialize(bson); + Assert.IsInstanceOf>(rehydrated.D); + Assert.IsInstanceOf>(rehydrated.ID); + Assert.IsInstanceOf>(rehydrated.SD); + Assert.IsInstanceOf>(rehydrated.SL); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + [Test] + public void TestTwoIntsWithIntKeys() { + var d = new Dictionary { { 1, 2 }, { 3, 4 } }; + var sd = CreateSortedDictionary(d); + var sl = CreateSortedList(d); + var obj = new T { D = d, ID = d, SD = sd, SL = sl }; + var json = obj.ToJson(); + var reps = new Dictionary { + { 1, "[1, 2]"}, + { 3, "[3, 4]"} + }; + var htRep = GetArrayRepresentationInKeyOrder(d, reps); + var sdRep = GetArrayRepresentationInKeyOrder(sd, reps); + var slRep = GetArrayRepresentationInKeyOrder(sl, reps); + var expected = "{ 'D' : #D, 'ID' : #D, 'SD' : #SD, 'SL' : #SL }"; + expected = expected.Replace("#D", htRep); + expected = expected.Replace("#SD", sdRep); + expected = expected.Replace("#SL", slRep); + expected = expected.Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = obj.ToBson(); + var rehydrated = BsonSerializer.Deserialize(bson); + Assert.IsInstanceOf>(rehydrated.D); + Assert.IsInstanceOf>(rehydrated.ID); + Assert.IsInstanceOf>(rehydrated.SD); + Assert.IsInstanceOf>(rehydrated.SL); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + [Test] + public void TestTwoStrings() { + var d = new Dictionary { { "A", "x" }, { "B", "y" } }; + var sd = CreateSortedDictionary(d); + var sl = CreateSortedList(d); + var obj = new T { D = d, ID = d, SD = sd, SL = sl }; + var json = obj.ToJson(); + var reps = new Dictionary { + { "A", "'x'"}, + { "B", "'y'"} + }; + var htRep = GetDocumentRepresentationInKeyOrder(d, reps); + var sdRep = GetDocumentRepresentationInKeyOrder(sd, reps); + var slRep = GetDocumentRepresentationInKeyOrder(sl, reps); + var expected = "{ 'D' : #D, 'ID' : #D, 'SD' : #SD, 'SL' : #SL }"; + expected = expected.Replace("#D", htRep); + expected = expected.Replace("#SD", sdRep); + expected = expected.Replace("#SL", slRep); + expected = expected.Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = obj.ToBson(); + var rehydrated = BsonSerializer.Deserialize(bson); + Assert.IsInstanceOf>(rehydrated.D); + Assert.IsInstanceOf>(rehydrated.ID); + Assert.IsInstanceOf>(rehydrated.SD); + Assert.IsInstanceOf>(rehydrated.SL); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + [Test] + public void TestTwoStringsWithIntKeys() { + var d = new Dictionary { { 1, "x" }, { 2, "y" } }; + var sd = CreateSortedDictionary(d); + var sl = CreateSortedList(d); + var obj = new T { D = d, ID = d, SD = sd, SL = sl }; + var json = obj.ToJson(); + var reps = new Dictionary { + { 1, "[1, 'x']"}, + { 2, "[2, 'y']"} + }; + var htRep = GetArrayRepresentationInKeyOrder(d, reps); + var sdRep = GetArrayRepresentationInKeyOrder(sd, reps); + var slRep = GetArrayRepresentationInKeyOrder(sl, reps); + var expected = "{ 'D' : #D, 'ID' : #D, 'SD' : #SD, 'SL' : #SL }"; + expected = expected.Replace("#D", htRep); + expected = expected.Replace("#SD", sdRep); + expected = expected.Replace("#SL", slRep); + expected = expected.Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = obj.ToBson(); + var rehydrated = BsonSerializer.Deserialize(bson); + Assert.IsInstanceOf>(rehydrated.D); + Assert.IsInstanceOf>(rehydrated.ID); + Assert.IsInstanceOf>(rehydrated.SD); + Assert.IsInstanceOf>(rehydrated.SL); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + [Test] + public void TestMixedPrimitiveTypes() { + var dateTime = DateTime.SpecifyKind(new DateTime(2010, 1, 1, 11, 22, 33), DateTimeKind.Utc); + var millis = (long) ((dateTime - BsonConstants.UnixEpoch).TotalMilliseconds); + var guid = Guid.Empty; + var objectId = ObjectId.Empty; + var d = new Dictionary { + { "A", true }, + { "B", dateTime }, + { "C", 1.5 }, + { "D", 1 }, + { "E", 2L }, + { "F", guid }, + { "G", objectId }, + { "H", "x" } + }; + var sd = CreateSortedDictionary(d); + var sl = CreateSortedList(d); + var obj = new T { D = d, ID = d, SD = sd, SL = sl }; + var json = obj.ToJson(); + var reps = new Dictionary { + { "A", "true" }, + { "B", "{ '$date' : #ms }".Replace("#ms", millis.ToString()) }, + { "C", "1.5" }, + { "D", "1" }, + { "E", "2" }, + { "F", "{ '$binary' : 'AAAAAAAAAAAAAAAAAAAAAA==', '$type' : '03' }" }, + { "G", "{ '$oid' : '000000000000000000000000' }" }, + { "H", "'x'" } + }; + var htRep = GetDocumentRepresentationInKeyOrder(d, reps); + var sdRep = GetDocumentRepresentationInKeyOrder(sd, reps); + var slRep = GetDocumentRepresentationInKeyOrder(sl, reps); + var expected = "{ 'D' : #D, 'ID' : #D, 'SD' : #SD, 'SL' : #SL }"; + expected = expected.Replace("#D", htRep); + expected = expected.Replace("#SD", sdRep); + expected = expected.Replace("#SL", slRep); + expected = expected.Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = obj.ToBson(); + var rehydrated = BsonSerializer.Deserialize(bson); + Assert.IsInstanceOf>(rehydrated.D); + Assert.IsInstanceOf>(rehydrated.ID); + Assert.IsInstanceOf>(rehydrated.SD); + Assert.IsInstanceOf>(rehydrated.SL); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + [Test] + public void TestMixedPrimitiveTypesWithIntKeys() { + var dateTime = DateTime.SpecifyKind(new DateTime(2010, 1, 1, 11, 22, 33), DateTimeKind.Utc); + var millis = (long) ((dateTime - BsonConstants.UnixEpoch).TotalMilliseconds); + var guid = Guid.Empty; + var objectId = ObjectId.Empty; + var d = new Dictionary { + { 1, true }, + { 2, dateTime }, + { 3, 1.5 }, + { 4, 1 }, + { 5, 2L }, + { 6, guid }, + { 7, objectId }, + { 8, "x" } + }; + var sd = CreateSortedDictionary(d); + var sl = CreateSortedList(d); + var obj = new T { D = d, ID = d, SD = sd, SL = sl }; + var json = obj.ToJson(); + var reps = new Dictionary { + { 1, "[1, true]" }, + { 2, "[2, { '$date' : #ms }]".Replace("#ms", millis.ToString()) }, + { 3, "[3, 1.5]" }, + { 4, "[4, 1]" }, + { 5, "[5, 2]" }, + { 6, "[6, { '$binary' : 'AAAAAAAAAAAAAAAAAAAAAA==', '$type' : '03' }]" }, + { 7, "[7, { '$oid' : '000000000000000000000000' }]" }, + { 8, "[8, 'x']" } + }; + var htRep = GetArrayRepresentationInKeyOrder(d, reps); + var sdRep = GetArrayRepresentationInKeyOrder(sd, reps); + var slRep = GetArrayRepresentationInKeyOrder(sl, reps); + var expected = "{ 'D' : #D, 'ID' : #D, 'SD' : #SD, 'SL' : #SL }"; + expected = expected.Replace("#D", htRep); + expected = expected.Replace("#SD", sdRep); + expected = expected.Replace("#SL", slRep); + expected = expected.Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = obj.ToBson(); + var rehydrated = BsonSerializer.Deserialize(bson); + Assert.IsInstanceOf>(rehydrated.D); + Assert.IsInstanceOf>(rehydrated.ID); + Assert.IsInstanceOf>(rehydrated.SD); + Assert.IsInstanceOf>(rehydrated.SL); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + [Test] + public void TestMixedPrimitiveTypesWithMixedKeys() { + // note: no SortedDictionary or SortedList in this test because you can't sort a set of keys that have mixed types + var dateTime = DateTime.SpecifyKind(new DateTime(2010, 1, 1, 11, 22, 33), DateTimeKind.Utc); + var millis = (long) ((dateTime - BsonConstants.UnixEpoch).TotalMilliseconds); + var guid = Guid.Empty; + var objectId = ObjectId.Empty; + var d = new Dictionary { + { "A", true }, + { "B", dateTime }, + { "C", 1.5 }, + { "D", 1 }, + { 4, 2L }, + { 5.0, guid }, + { true, objectId }, + { false, "x" } + }; + var obj = new T { D = d, ID = d, SD = null, SL = null }; + var json = obj.ToJson(); + var reps = new Dictionary { + { "A", "['A', true]" }, + { "B", "['B', { '$date' : #ms }]".Replace("#ms", millis.ToString()) }, + { "C", "['C', 1.5]" }, + { "D", "['D', 1]" }, + { 4, "[4, 2]" }, + { 5.0, "[5, { '$binary' : 'AAAAAAAAAAAAAAAAAAAAAA==', '$type' : '03' }]" }, + { true, "[true, { '$oid' : '000000000000000000000000' }]" }, + { false, "[false, 'x']" } + }; + var htRep = GetArrayRepresentationInKeyOrder(d, reps); + var expected = "{ 'D' : #D, 'ID' : #D, 'SD' : null, 'SL' : null }"; + expected = expected.Replace("#D", htRep); + expected = expected.Replace("'", "\""); + Assert.AreEqual(expected, json); + + var bson = obj.ToBson(); + var rehydrated = BsonSerializer.Deserialize(bson); + Assert.IsInstanceOf>(rehydrated.D); + Assert.IsInstanceOf>(rehydrated.ID); + Assert.IsNull(rehydrated.SD); + Assert.IsNull(rehydrated.SL); + Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson())); + } + + private SortedDictionary CreateSortedDictionary( + Dictionary d + ) { + var sd = new SortedDictionary(); + foreach (KeyValuePair entry in d) { + sd.Add(entry.Key, entry.Value); + } + return sd; + } + + private SortedList CreateSortedList( + Dictionary d + ) { + var sl = new SortedList(); + foreach (KeyValuePair entry in d) { + sl.Add(entry.Key, entry.Value); + } + return sl; + } + + private string GetArrayRepresentationInKeyOrder( + IDictionary dictionary, + IDictionary representations + ) { + var sb = new StringBuilder(); + foreach (var key in dictionary.Keys) { + sb.Append((sb.Length == 0) ? "[" : ", "); + sb.Append(representations[key]); + } + sb.Append("]"); + return sb.ToString(); + } + + private string GetDocumentRepresentationInKeyOrder( + IDictionary dictionary, + IDictionary representations + ) { + var sb = new StringBuilder(); + foreach (var key in dictionary.Keys) { + sb.Append((sb.Length == 0) ? "{ " : ", "); + sb.AppendFormat("'{0}' : {1}", key, representations[key]); + } + sb.Append(" }"); + return sb.ToString(); + } + } +}