private void DeserializeMember(BsonReader bsonReader, object obj, BsonMemberMap memberMap) { try { var nominalType = memberMap.MemberType; Type actualType; if (bsonReader.CurrentBsonType == BsonType.Null) { actualType = nominalType; } else { var discriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(nominalType); actualType = discriminatorConvention.GetActualType(bsonReader, nominalType); // returns nominalType if no discriminator found } var serializer = memberMap.GetSerializer(actualType); var value = serializer.Deserialize(bsonReader, nominalType, actualType, memberMap.SerializationOptions); memberMap.Setter(obj, value); } catch (Exception ex) { var message = string.Format( "An error occurred while deserializing the {0} {1} of class {2}: {3}", // terminating period provided by nested message memberMap.MemberName, (memberMap.MemberInfo.MemberType == MemberTypes.Field) ? "field" : "property", obj.GetType().FullName, ex.Message); throw new FileFormatException(message, ex); } }
/// <summary> /// Deserializes an object from a BsonReader. /// </summary> /// <param name="bsonReader">The BsonReader.</param> /// <param name="nominalType">The nominal type of the object.</param> /// <param name="options">The serialization options.</param> /// <returns>An object.</returns> public object Deserialize( BsonReader bsonReader, Type nominalType, IBsonSerializationOptions options ) { VerifyNominalType(nominalType); if (bsonReader.CurrentBsonType == Bson.BsonType.Null) { bsonReader.ReadNull(); return(null); } else { var discriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(nominalType); var actualType = discriminatorConvention.GetActualType(bsonReader, nominalType); if (actualType != nominalType) { var serializer = BsonSerializer.LookupSerializer(actualType); if (serializer != this) { // in rare cases a concrete actualType might have a more specialized serializer return(serializer.Deserialize(bsonReader, nominalType, actualType, options)); } } return(Deserialize(bsonReader, nominalType, actualType, options)); } }
/// <summary> /// Deserializes an object from a BsonReader. /// </summary> /// <param name="bsonReader">The BsonReader.</param> /// <param name="nominalType">The nominal type of the object.</param> /// <param name="options">The serialization options.</param> /// <returns>An object.</returns> public static object Deserialize(BsonReader bsonReader, Type nominalType, IBsonSerializationOptions options) { if (nominalType == typeof(BsonDocument)) { var bsonDocument = new BsonDocument(); return(bsonDocument.Deserialize(bsonReader, nominalType, options)); } // if nominalType is an interface find out the actualType and use it instead if (nominalType.IsInterface) { var discriminatorConvention = BsonDefaultSerializer.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 FileFormatException(message); } var serializer = LookupSerializer(actualType); return(serializer.Deserialize(bsonReader, actualType, options)); } else { var serializer = LookupSerializer(nominalType); return(serializer.Deserialize(bsonReader, nominalType, options)); } }
/// <summary> /// Looks up the discriminator convention for a type. /// </summary> /// <param name="type">The type.</param> /// <returns>A discriminator convention.</returns> public static IDiscriminatorConvention LookupDiscriminatorConvention( Type type ) { lock (BsonSerializer.ConfigLock) { 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) { BsonDefaultSerializer.RegisterDiscriminatorConvention(unregisteredType, convention); unregisteredType = unregisteredType.BaseType; } } } return(convention); } }
// internal methods /// <summary> /// Gets the discriminator convention for the member type. /// </summary> /// <returns>The discriminator convention for the member type.</returns> internal IDiscriminatorConvention GetDiscriminatorConvention() { // return a cached discriminator convention when possible var discriminatorConvention = _cachedDiscriminatorConvention; if (discriminatorConvention == null) { // it's possible but harmless for multiple threads to do the initial lookup at the same time discriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(_memberType); _cachedDiscriminatorConvention = discriminatorConvention; } return(discriminatorConvention); }
private void DeserializeMember( BsonReader bsonReader, object obj, BsonMemberMap memberMap ) { var nominalType = memberMap.MemberType; Type actualType; if (bsonReader.CurrentBsonType == BsonType.Null) { actualType = nominalType; } else { var discriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(nominalType); actualType = discriminatorConvention.GetActualType(bsonReader, nominalType); // returns nominalType if no discriminator found } var serializer = memberMap.GetSerializer(actualType); var value = serializer.Deserialize(bsonReader, nominalType, actualType, memberMap.SerializationOptions); memberMap.Setter(obj, value); }
/// <summary> /// Serializes an object to a BsonWriter. /// </summary> /// <param name="bsonWriter">The BsonWriter.</param> /// <param name="nominalType">The nominal type.</param> /// <param name="value">The object.</param> /// <param name="options">The serialization options.</param> public void Serialize( BsonWriter bsonWriter, Type nominalType, object value, IBsonSerializationOptions options ) { if (value == null) { bsonWriter.WriteNull(); } else { // Nullable types are weird because they get boxed as their underlying value type // we can best handle that by switching the nominalType to the underlying value type // (so VerifyNominalType doesn't fail and we don't get an unnecessary discriminator) if (nominalType.IsGenericType && nominalType.GetGenericTypeDefinition() == typeof(Nullable <>)) { nominalType = nominalType.GetGenericArguments()[0]; } VerifyNominalType(nominalType); var actualType = (value == null) ? nominalType : value.GetType(); var classMap = BsonClassMap.LookupClassMap(actualType); bsonWriter.WriteStartDocument(); var documentOptions = (options == null) ? DocumentSerializationOptions.Defaults : (DocumentSerializationOptions)options; BsonMemberMap idMemberMap = null; if (documentOptions.SerializeIdFirst) { idMemberMap = classMap.IdMemberMap; if (idMemberMap != null) { SerializeMember(bsonWriter, value, idMemberMap); } } if (actualType != nominalType || classMap.DiscriminatorIsRequired || classMap.HasRootClass) { // never write out a discriminator for an anonymous class if (!classMap.IsAnonymous) { var discriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(nominalType); var discriminator = discriminatorConvention.GetDiscriminator(nominalType, actualType); if (discriminator != null) { bsonWriter.WriteName(discriminatorConvention.ElementName); discriminator.WriteTo(bsonWriter); } } } foreach (var memberMap in classMap.MemberMaps) { // note: if serializeIdFirst is false then idMemberMap will be null (so no property will be skipped) if (memberMap != idMemberMap) { if (memberMap == classMap.ExtraElementsMemberMap) { SerializeExtraElements(bsonWriter, value, memberMap); } else { SerializeMember(bsonWriter, value, memberMap); } } } bsonWriter.WriteEndDocument(); } }
/// <summary> /// Deserializes an object from a BsonReader. /// </summary> /// <param name="bsonReader">The BsonReader.</param> /// <param name="nominalType">The nominal type of the object.</param> /// <param name="actualType">The actual type of the object.</param> /// <param name="options">The serialization options.</param> /// <returns>An object.</returns> public object Deserialize( BsonReader bsonReader, Type nominalType, Type actualType, IBsonSerializationOptions options ) { VerifyNominalType(nominalType); if (bsonReader.CurrentBsonType == Bson.BsonType.Null) { bsonReader.ReadNull(); return(null); } else { if (actualType.IsValueType) { var message = string.Format("Value class {0} cannot be deserialized.", actualType.FullName); throw new BsonSerializationException(message); } var classMap = BsonClassMap.LookupClassMap(actualType); if (classMap.IsAnonymous) { throw new InvalidOperationException("An anonymous class cannot be deserialized."); } var obj = classMap.CreateInstance(); bsonReader.ReadStartDocument(); var missingElementMemberMaps = new HashSet <BsonMemberMap>(classMap.MemberMaps); // make a copy! var discriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(nominalType); while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) { var elementName = bsonReader.ReadName(); if (elementName == discriminatorConvention.ElementName) { bsonReader.SkipValue(); // skip over discriminator continue; } var memberMap = classMap.GetMemberMapForElement(elementName); if (memberMap != null && memberMap != classMap.ExtraElementsMemberMap) { DeserializeMember(bsonReader, obj, memberMap); missingElementMemberMaps.Remove(memberMap); } else { if (classMap.ExtraElementsMemberMap != null) { DeserializeExtraElement(bsonReader, obj, elementName, classMap.ExtraElementsMemberMap); } else if (classMap.IgnoreExtraElements) { bsonReader.SkipValue(); } else { var message = string.Format("Element '{0}' does not match any field or property of class {1}.", elementName, classMap.ClassType.FullName); throw new FileFormatException(message); } } } bsonReader.ReadEndDocument(); foreach (var memberMap in missingElementMemberMaps) { if (memberMap.IsRequired) { var fieldOrProperty = (memberMap.MemberInfo.MemberType == MemberTypes.Field) ? "field" : "property"; var message = string.Format("Required element '{0}' for {1} '{2}' of class {3} is missing.", memberMap.ElementName, fieldOrProperty, memberMap.MemberName, classMap.ClassType.FullName); throw new FileFormatException(message); } if (memberMap.HasDefaultValue) { memberMap.ApplyDefaultValue(obj); } } return(obj); } }
/// <summary> /// Deserializes an object from a BsonReader. /// </summary> /// <param name="bsonReader">The BsonReader.</param> /// <param name="nominalType">The nominal type of the object.</param> /// <param name="actualType">The actual type of the object.</param> /// <param name="options">The serialization options.</param> /// <returns>An object.</returns> public object Deserialize( BsonReader bsonReader, Type nominalType, Type actualType, IBsonSerializationOptions options ) { VerifyNominalType(nominalType); if (bsonReader.CurrentBsonType == Bson.BsonType.Null) { bsonReader.ReadNull(); return(null); } else { var classMap = BsonClassMap.LookupClassMap(actualType); if (classMap.IsAnonymous) { throw new InvalidOperationException("Anonymous classes cannot be deserialized"); } var obj = classMap.CreateInstance(); bsonReader.ReadStartDocument(); var missingElementMemberMaps = new HashSet <BsonMemberMap>(classMap.MemberMaps); // make a copy! var discriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(nominalType); while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) { var elementName = bsonReader.ReadName(); if (elementName == discriminatorConvention.ElementName) { bsonReader.SkipValue(); // skip over discriminator continue; } var memberMap = classMap.GetMemberMapForElement(elementName); if (memberMap != null && memberMap != classMap.ExtraElementsMemberMap) { DeserializeMember(bsonReader, obj, memberMap); missingElementMemberMaps.Remove(memberMap); } else { if (classMap.ExtraElementsMemberMap != null) { DeserializeExtraElement(bsonReader, obj, elementName, classMap.ExtraElementsMemberMap); } else if (classMap.IgnoreExtraElements) { bsonReader.SkipValue(); } else { string message = string.Format("Unexpected element: {0}", elementName); throw new FileFormatException(message); } } } bsonReader.ReadEndDocument(); foreach (var memberMap in missingElementMemberMaps) { if (memberMap.IsRequired) { var message = string.Format("Required element is missing: {0}", memberMap.ElementName); throw new FileFormatException(message); } if (memberMap.HasDefaultValue) { memberMap.ApplyDefaultValue(obj); } } return(obj); } }