/* 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);
}
}
}