/* Copyright 2010-present 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.Dynamic; using System.IO; using System.Linq.Expressions; using MongoDB.Bson.IO; namespace MongoDB.Bson.Serialization.Serializers { /// /// Base serializer for dynamic types. /// /// The dynamic type. public abstract class DynamicDocumentBaseSerializer : SerializerBase where T : IDynamicMetaObjectProvider { // private static fields private static readonly IBsonSerializer _objectSerializer = BsonSerializer.LookupSerializer(); // constructors /// /// Initializes a new instance of the class. /// protected DynamicDocumentBaseSerializer() { } // public methods /// /// Deserializes a value. /// /// The deserialization context. /// The deserialization args. /// A deserialized value. public override T Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { var bsonReader = context.Reader; var bsonType = bsonReader.GetCurrentBsonType(); string message; switch (bsonType) { case BsonType.Document: var dynamicContext = context.With(ConfigureDeserializationContext); bsonReader.ReadStartDocument(); var document = CreateDocument(); while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) { var name = bsonReader.ReadName(); var value = _objectSerializer.Deserialize(dynamicContext); SetValueForMember(document, name, value); } bsonReader.ReadEndDocument(); return document; default: message = string.Format("Cannot deserialize a '{0}' from BsonType '{1}'.", BsonUtils.GetFriendlyTypeName(typeof(T)), bsonType); throw new FormatException(message); } } /// /// Serializes a value. /// /// The serialization context. /// The serialization args. /// The object. public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, T value) { var bsonWriter = context.Writer; var metaObject = value.GetMetaObject(Expression.Constant(value)); var memberNames = metaObject.GetDynamicMemberNames(); var dynamicContext = context.With(ConfigureSerializationContext); bsonWriter.WriteStartDocument(); foreach (var memberName in memberNames) { object memberValue; if (TryGetValueForMember(value, memberName, out memberValue)) { bsonWriter.WriteName(memberName); _objectSerializer.Serialize(dynamicContext, memberValue); } } bsonWriter.WriteEndDocument(); } // protected methods /// /// Configures the deserialization context. /// /// The builder. protected abstract void ConfigureDeserializationContext(BsonDeserializationContext.Builder builder); /// /// Configures the serialization context. /// /// The builder. protected abstract void ConfigureSerializationContext(BsonSerializationContext.Builder builder); /// /// Creates the document. /// /// A protected abstract T CreateDocument(); /// /// Sets the value for the member. /// /// The document. /// Name of the member. /// The value. protected abstract void SetValueForMember(T document, string memberName, object value); /// /// Tries to get the value for a member. Returns true if the member should be serialized. /// /// The document. /// Name of the member. /// The value. /// true if the member should be serialized; otherwise false. protected abstract bool TryGetValueForMember(T document, string memberName, out object value); } }