/* Copyright 2010-2014 MongoDB 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.Threading; // don't add using statement for MongoDB.Bson.Serialization.Serializers to minimize dependencies on DefaultSerializer using MongoDB.Bson.IO; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Conventions; using MongoDB.Bson.Serialization.IdGenerators; using MongoDB.Bson.Serialization.Serializers; namespace MongoDB.Bson.Serialization { /// /// A static class that represents the BSON serialization functionality. /// public static class BsonSerializer { // private static fields private static ReaderWriterLock __configLock = new ReaderWriterLock(); private static Dictionary __idGenerators = new Dictionary(); private static Dictionary __serializers = new Dictionary(); private static Dictionary __genericSerializerDefinitions = new Dictionary(); private static List __serializationProviders = new List(); private static Dictionary __discriminatorConventions = new Dictionary(); private static Dictionary> __discriminators = new Dictionary>(); private static HashSet __discriminatedTypes = new HashSet(); private static HashSet __typesWithRegisteredKnownTypes = new HashSet(); private static bool __useNullIdChecker = false; private static bool __useZeroIdChecker = false; // static constructor static BsonSerializer() { RegisterDefaultSerializationProviders(); RegisterIdGenerators(); } // public static properties /// /// Gets or sets whether to use the NullIdChecker on reference Id types that don't have an IdGenerator registered. /// public static bool UseNullIdChecker { get { return __useNullIdChecker; } set { __useNullIdChecker = value; } } /// /// Gets or sets whether to use the ZeroIdChecker on value Id types that don't have an IdGenerator registered. /// public static bool UseZeroIdChecker { get { return __useZeroIdChecker; } set { __useZeroIdChecker = value; } } // internal static properties internal static ReaderWriterLock ConfigLock { get { return __configLock; } } // public static methods /// /// Deserializes an object from a BsonDocument. /// /// The nominal type of the object. /// The BsonDocument. /// A TNominalType. public static TNominalType Deserialize(BsonDocument document) { return (TNominalType)Deserialize(document, typeof(TNominalType)); } /// /// Deserializes an object from a JsonBuffer. /// /// The nominal type of the object. /// The JsonBuffer. /// A TNominalType. public static TNominalType Deserialize(JsonBuffer buffer) { return (TNominalType)Deserialize(buffer, typeof(TNominalType)); } /// /// Deserializes an object from a BsonReader. /// /// The nominal type of the object. /// The BsonReader. /// A TNominalType. public static TNominalType Deserialize(BsonReader bsonReader) { return (TNominalType)Deserialize(bsonReader, typeof(TNominalType)); } /// /// Deserializes an object from a BSON byte array. /// /// The nominal type of the object. /// The BSON byte array. /// A TNominalType. public static TNominalType Deserialize(byte[] bytes) { return (TNominalType)Deserialize(bytes, typeof(TNominalType)); } /// /// Deserializes an object from a BSON Stream. /// /// The nominal type of the object. /// The BSON Stream. /// A TNominalType. public static TNominalType Deserialize(Stream stream) { return (TNominalType)Deserialize(stream, typeof(TNominalType)); } /// /// Deserializes an object from a JSON string. /// /// The nominal type of the object. /// The JSON string. /// A TNominalType. public static TNominalType Deserialize(string json) { return (TNominalType)Deserialize(json, typeof(TNominalType)); } /// /// Deserializes an object from a JSON TextReader. /// /// The nominal type of the object. /// The JSON TextReader. /// A TNominalType. public static TNominalType Deserialize(TextReader textReader) { return (TNominalType)Deserialize(textReader, typeof(TNominalType)); } /// /// Deserializes an object from a BsonDocument. /// /// The BsonDocument. /// The nominal type of the object. /// A TNominalType. public static object Deserialize(BsonDocument document, Type nominalType) { using (var bsonReader = BsonReader.Create(document)) { return Deserialize(bsonReader, nominalType); } } /// /// Deserializes an object from a JsonBuffer. /// /// The JsonBuffer. /// The nominal type of the object. /// An object. public static object Deserialize(JsonBuffer buffer, Type nominalType) { using (var bsonReader = BsonReader.Create(buffer)) { return Deserialize(bsonReader, nominalType); } } /// /// Deserializes an object from a BsonReader. /// /// The BsonReader. /// The nominal type of the object. /// An object. public static object Deserialize(BsonReader bsonReader, Type nominalType) { return Deserialize(bsonReader, nominalType, null); } /// /// Deserializes an object from a BsonReader. /// /// The BsonReader. /// The nominal type of the object. /// The serialization options. /// An object. public static object Deserialize(BsonReader bsonReader, Type nominalType, IBsonSerializationOptions options) { // if nominalType is an interface find out the actualType and use it instead if (nominalType.IsInterface) { var discriminatorConvention = LookupDiscriminatorConvention(nominalType); var actualType = discriminatorConvention.GetActualType(bsonReader, nominalType); if (actualType == nominalType) { var message = string.Format("Unable to determine actual type of object to deserialize. NominalType is the interface {0}.", nominalType); throw new Exception(message); } var serializer = LookupSerializer(actualType); return serializer.Deserialize(bsonReader, actualType, options); } else { var serializer = LookupSerializer(nominalType); return serializer.Deserialize(bsonReader, nominalType, options); } } /// /// Deserializes an object from a BSON byte array. /// /// The BSON byte array. /// The nominal type of the object. /// An object. public static object Deserialize(byte[] bytes, Type nominalType) { using (var memoryStream = new MemoryStream(bytes)) { return Deserialize(memoryStream, nominalType); } } /// /// Deserializes an object from a BSON Stream. /// /// The BSON Stream. /// The nominal type of the object. /// An object. public static object Deserialize(Stream stream, Type nominalType) { using (var bsonReader = BsonReader.Create(stream)) { return Deserialize(bsonReader, nominalType); } } /// /// Deserializes an object from a JSON string. /// /// The JSON string. /// The nominal type of the object. /// An object. public static object Deserialize(string json, Type nominalType) { using (var bsonReader = BsonReader.Create(json)) { return Deserialize(bsonReader, nominalType); } } /// /// Deserializes an object from a JSON TextReader. /// /// The JSON TextReader. /// The nominal type of the object. /// An object. public static object Deserialize(TextReader textReader, Type nominalType) { using (var bsonReader = BsonReader.Create(textReader)) { return Deserialize(bsonReader, nominalType); } } /// /// Returns whether the given type has any discriminators registered for any of its subclasses. /// /// A Type. /// True if the type is discriminated. public static bool IsTypeDiscriminated(Type type) { return type.IsInterface || __discriminatedTypes.Contains(type); } /// /// Looks up the actual type of an object to be deserialized. /// /// The nominal type of the object. /// The discriminator. /// The actual type of the object. public static Type LookupActualType(Type nominalType, BsonValue discriminator) { if (discriminator == null) { return nominalType; } // note: EnsureKnownTypesAreRegistered handles its own locking so call from outside any lock EnsureKnownTypesAreRegistered(nominalType); __configLock.EnterReadLock(); try { Type actualType = null; HashSet hashSet; if (__discriminators.TryGetValue(discriminator, out hashSet)) { foreach (var type in hashSet) { if (nominalType.IsAssignableFrom(type)) { if (actualType == null) { actualType = type; } else { string message = string.Format("Ambiguous discriminator '{0}'.", discriminator); throw new BsonSerializationException(message); } } } } if (actualType == null && discriminator.IsString) { actualType = TypeNameDiscriminator.GetActualType(discriminator.AsString); // see if it's a Type name } if (actualType == null) { string message = string.Format("Unknown discriminator value '{0}'.", discriminator); throw new BsonSerializationException(message); } if (!nominalType.IsAssignableFrom(actualType)) { string message = string.Format( "Actual type {0} is not assignable to expected type {1}.", actualType.FullName, nominalType.FullName); throw new BsonSerializationException(message); } return actualType; } finally { __configLock.ExitReadLock(); } } /// /// Looks up the discriminator convention for a type. /// /// The type. /// A discriminator convention. public static IDiscriminatorConvention LookupDiscriminatorConvention(Type type) { __configLock.EnterReadLock(); try { IDiscriminatorConvention convention; if (__discriminatorConventions.TryGetValue(type, out convention)) { return convention; } } finally { __configLock.ExitReadLock(); } __configLock.EnterWriteLock(); try { IDiscriminatorConvention convention; if (!__discriminatorConventions.TryGetValue(type, out convention)) { // if there is no convention registered for object register the default one if (!__discriminatorConventions.ContainsKey(typeof(object))) { var defaultDiscriminatorConvention = StandardDiscriminatorConvention.Hierarchical; __discriminatorConventions.Add(typeof(object), defaultDiscriminatorConvention); if (type == typeof(object)) { return defaultDiscriminatorConvention; } } if (type.IsInterface) { // TODO: should convention for interfaces be inherited from parent interfaces? convention = __discriminatorConventions[typeof(object)]; __discriminatorConventions[type] = convention; } else { // inherit the discriminator convention from the closest parent that has one Type parentType = type.BaseType; while (convention == null) { if (parentType == null) { var message = string.Format("No discriminator convention found for type {0}.", type.FullName); throw new BsonSerializationException(message); } if (__discriminatorConventions.TryGetValue(parentType, out convention)) { break; } parentType = parentType.BaseType; } // register this convention for all types between this and the parent type where we found the convention var unregisteredType = type; while (unregisteredType != parentType) { RegisterDiscriminatorConvention(unregisteredType, convention); unregisteredType = unregisteredType.BaseType; } } } return convention; } finally { __configLock.ExitWriteLock(); } } /// /// Looks up a generic serializer definition. /// /// The generic type. /// A generic serializer definition. public static Type LookupGenericSerializerDefinition(Type genericTypeDefinition) { __configLock.EnterReadLock(); try { Type genericSerializerDefinition; __genericSerializerDefinitions.TryGetValue(genericTypeDefinition, out genericSerializerDefinition); return genericSerializerDefinition; } finally { __configLock.ExitReadLock(); } } /// /// Looks up an IdGenerator. /// /// The Id type. /// An IdGenerator for the Id type. public static IIdGenerator LookupIdGenerator(Type type) { __configLock.EnterReadLock(); try { IIdGenerator idGenerator; if (__idGenerators.TryGetValue(type, out idGenerator)) { return idGenerator; } } finally { __configLock.ExitReadLock(); } __configLock.EnterWriteLock(); try { IIdGenerator idGenerator; if (!__idGenerators.TryGetValue(type, out idGenerator)) { if (type.IsValueType && __useZeroIdChecker) { var iEquatableDefinition = typeof(IEquatable<>); var iEquatableType = iEquatableDefinition.MakeGenericType(type); if (iEquatableType.IsAssignableFrom(type)) { var zeroIdCheckerDefinition = typeof(ZeroIdChecker<>); var zeroIdCheckerType = zeroIdCheckerDefinition.MakeGenericType(type); idGenerator = (IIdGenerator)Activator.CreateInstance(zeroIdCheckerType); } } else if (__useNullIdChecker) { idGenerator = NullIdChecker.Instance; } else { idGenerator = null; } __idGenerators[type] = idGenerator; // remember it even if it's null } return idGenerator; } finally { __configLock.ExitWriteLock(); } } /// /// Looks up a serializer for a Type. /// /// The Type. /// A serializer for the Type. public static IBsonSerializer LookupSerializer(Type type) { // since we don't allow registering serializers for BsonDocument no lookup is needed if (type == typeof(BsonDocument)) { return BsonDocumentSerializer.Instance; } __configLock.EnterReadLock(); try { IBsonSerializer serializer; if (__serializers.TryGetValue(type, out serializer)) { return serializer; } } finally { __configLock.ExitReadLock(); } __configLock.EnterWriteLock(); try { IBsonSerializer serializer; if (!__serializers.TryGetValue(type, out serializer)) { if (serializer == null) { var serializerAttributes = type.GetCustomAttributes(typeof(BsonSerializerAttribute), false); // don't inherit if (serializerAttributes.Length == 1) { var serializerAttribute = (BsonSerializerAttribute)serializerAttributes[0]; serializer = serializerAttribute.CreateSerializer(type); } } if (serializer == null && type.IsGenericType) { var genericTypeDefinition = type.GetGenericTypeDefinition(); var genericSerializerDefinition = LookupGenericSerializerDefinition(genericTypeDefinition); if (genericSerializerDefinition != null) { var genericSerializerType = genericSerializerDefinition.MakeGenericType(type.GetGenericArguments()); serializer = (IBsonSerializer)Activator.CreateInstance(genericSerializerType); } } if (serializer == null) { foreach (var serializationProvider in __serializationProviders) { serializer = serializationProvider.GetSerializer(type); if (serializer != null) { break; } } } if (serializer == null) { var message = string.Format("No serializer found for type {0}.", type.FullName); throw new BsonSerializationException(message); } __serializers[type] = serializer; } return serializer; } finally { __configLock.ExitWriteLock(); } } /// /// Registers the discriminator for a type. /// /// The type. /// The discriminator. public static void RegisterDiscriminator(Type type, BsonValue discriminator) { if (type.IsInterface) { var message = string.Format("Discriminators can only be registered for classes, not for interface {0}.", type.FullName); throw new BsonSerializationException(message); } __configLock.EnterWriteLock(); try { HashSet hashSet; if (!__discriminators.TryGetValue(discriminator, out hashSet)) { hashSet = new HashSet(); __discriminators.Add(discriminator, hashSet); } if (!hashSet.Contains(type)) { hashSet.Add(type); // mark all base types as discriminated (so we know that it's worth reading a discriminator) for (var baseType = type.BaseType; baseType != null; baseType = baseType.BaseType) { __discriminatedTypes.Add(baseType); } } } finally { __configLock.ExitWriteLock(); } } /// /// Registers the discriminator convention for a type. /// /// Type type. /// The discriminator convention. public static void RegisterDiscriminatorConvention(Type type, IDiscriminatorConvention convention) { __configLock.EnterWriteLock(); try { if (!__discriminatorConventions.ContainsKey(type)) { __discriminatorConventions.Add(type, convention); } else { var message = string.Format("There is already a discriminator convention registered for type {0}.", type.FullName); throw new BsonSerializationException(message); } } finally { __configLock.ExitWriteLock(); } } /// /// Registers a generic serializer definition for a generic type. /// /// The generic type. /// The generic serializer definition. public static void RegisterGenericSerializerDefinition( Type genericTypeDefinition, Type genericSerializerDefinition) { __configLock.EnterWriteLock(); try { __genericSerializerDefinitions[genericTypeDefinition] = genericSerializerDefinition; } finally { __configLock.ExitWriteLock(); } } /// /// Registers an IdGenerator for an Id Type. /// /// The Id Type. /// The IdGenerator for the Id Type. public static void RegisterIdGenerator(Type type, IIdGenerator idGenerator) { __configLock.EnterWriteLock(); try { __idGenerators[type] = idGenerator; } finally { __configLock.ExitWriteLock(); } } /// /// Registers a serialization provider. /// /// The serialization provider. public static void RegisterSerializationProvider(IBsonSerializationProvider provider) { __configLock.EnterWriteLock(); try { // add new provider to the front of the list __serializationProviders.Insert(0, provider); } finally { __configLock.ExitWriteLock(); } } /// /// Registers a serializer for a type. /// /// The type. /// The serializer. public static void RegisterSerializer(Type type, IBsonSerializer serializer) { // don't allow a serializer to be registered for subclasses of BsonValue if (typeof(BsonValue).IsAssignableFrom(type)) { var message = string.Format("A serializer cannot be registered for type {0} because it is a subclass of BsonValue.", BsonUtils.GetFriendlyTypeName(type)); throw new BsonSerializationException(message); } __configLock.EnterWriteLock(); try { if (__serializers.ContainsKey(type)) { var message = string.Format("There is already a serializer registered for type {0}.", type.FullName); throw new BsonSerializationException(message); } __serializers.Add(type, serializer); } finally { __configLock.ExitWriteLock(); } } /// /// Serializes an object to a BsonWriter. /// /// The nominal type of the object. /// The BsonWriter. /// The object. public static void Serialize(BsonWriter bsonWriter, TNominalType value) { Serialize(bsonWriter, value, null); } /// /// Serializes an object to a BsonWriter. /// /// The nominal type of the object. /// The BsonWriter. /// The object. /// The serialization options. public static void Serialize( BsonWriter bsonWriter, TNominalType value, IBsonSerializationOptions options) { Serialize(bsonWriter, typeof(TNominalType), value, options); } /// /// Serializes an object to a BsonWriter. /// /// The BsonWriter. /// The nominal type of the object. /// The object. public static void Serialize(BsonWriter bsonWriter, Type nominalType, object value) { Serialize(bsonWriter, nominalType, value, null); } /// /// Serializes an object to a BsonWriter. /// /// The BsonWriter. /// The nominal type of the object. /// The object. /// The serialization options. public static void Serialize( BsonWriter bsonWriter, Type nominalType, object value, IBsonSerializationOptions options) { var actualType = (value == null) ? nominalType : value.GetType(); var serializer = LookupSerializer(actualType); serializer.Serialize(bsonWriter, nominalType, value, options); } // internal static methods internal static void EnsureKnownTypesAreRegistered(Type nominalType) { __configLock.EnterReadLock(); try { if (__typesWithRegisteredKnownTypes.Contains(nominalType)) { return; } } finally { __configLock.ExitReadLock(); } __configLock.EnterWriteLock(); try { if (!__typesWithRegisteredKnownTypes.Contains(nominalType)) { // only call LookupClassMap for classes with a BsonKnownTypesAttribute var knownTypesAttribute = nominalType.GetCustomAttributes(typeof(BsonKnownTypesAttribute), false); if (knownTypesAttribute != null && knownTypesAttribute.Length > 0) { // try and force a scan of the known types LookupSerializer(nominalType); } __typesWithRegisteredKnownTypes.Add(nominalType); } } finally { __configLock.ExitWriteLock(); } } // private static methods private static void RegisterDefaultSerializationProviders() { // last one registered gets first chance at providing the serializer RegisterSerializationProvider(new BsonClassMapSerializationProvider()); RegisterSerializationProvider(new BsonDefaultSerializationProvider()); } private static void RegisterIdGenerators() { RegisterIdGenerator(typeof(BsonObjectId), BsonObjectIdGenerator.Instance); RegisterIdGenerator(typeof(Guid), GuidGenerator.Instance); RegisterIdGenerator(typeof(ObjectId), ObjectIdGenerator.Instance); } } }