/* 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.Collections; using System.Collections.Generic; using MongoDB.Bson.Serialization.Conventions; namespace MongoDB.Bson.Serialization.Serializers { /// /// Represents a base serializer for enumerable values. /// /// The type of the value. public abstract class EnumerableSerializerBase : SerializerBase, IBsonArraySerializer where TValue : class, IEnumerable { // private fields private readonly IDiscriminatorConvention _discriminatorConvention = new ScalarDiscriminatorConvention("_t"); private readonly Lazy _lazyItemSerializer; // constructors /// /// Initializes a new instance of the class. /// protected EnumerableSerializerBase() : this(BsonSerializer.SerializerRegistry) { } /// /// Initializes a new instance of the class. /// /// The item serializer. protected EnumerableSerializerBase(IBsonSerializer itemSerializer) { if (itemSerializer == null) { throw new ArgumentNullException("itemSerializer"); } _lazyItemSerializer = new Lazy(() => itemSerializer); } /// /// Initializes a new instance of the class. /// /// The serializer registry. protected EnumerableSerializerBase(IBsonSerializerRegistry serializerRegistry) { if (serializerRegistry == null) { throw new ArgumentNullException("serializerRegistry"); } _lazyItemSerializer = new Lazy(() => serializerRegistry.GetSerializer(typeof(object))); } /// /// Gets the item serializer. /// /// /// The item serializer. /// public IBsonSerializer ItemSerializer { get { return _lazyItemSerializer.Value; } } // public methods /// /// Deserializes a value. /// /// The deserialization context. /// The deserialization args. /// A deserialized value. public override TValue Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { var bsonReader = context.Reader; var bsonType = bsonReader.GetCurrentBsonType(); switch (bsonType) { case BsonType.Null: bsonReader.ReadNull(); return null; case BsonType.Array: bsonReader.ReadStartArray(); var accumulator = CreateAccumulator(); while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) { var item = _lazyItemSerializer.Value.Deserialize(context); AddItem(accumulator, item); } bsonReader.ReadEndArray(); return FinalizeResult(accumulator); case BsonType.Document: var serializer = new DiscriminatedWrapperSerializer(_discriminatorConvention, this); if (serializer.IsPositionedAtDiscriminatedWrapper(context)) { return (TValue)serializer.Deserialize(context); } else { goto default; } default: throw CreateCannotDeserializeFromBsonTypeException(bsonType); } } /// /// Tries to get the serialization info for the individual items of the array. /// /// The serialization information. /// /// true if the serialization info exists; otherwise false. /// public bool TryGetItemSerializationInfo(out BsonSerializationInfo serializationInfo) { var itemSerializer = _lazyItemSerializer.Value; serializationInfo = new BsonSerializationInfo(null, itemSerializer, itemSerializer.ValueType); return true; } /// /// Serializes a value. /// /// The serialization context. /// The serialization args. /// The object. public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TValue value) { var bsonWriter = context.Writer; if (value == null) { bsonWriter.WriteNull(); } else { var actualType = value.GetType(); if (actualType == args.NominalType || args.SerializeAsNominalType) { bsonWriter.WriteStartArray(); foreach (var item in EnumerateItemsInSerializationOrder(value)) { _lazyItemSerializer.Value.Serialize(context, item); } bsonWriter.WriteEndArray(); } else { var serializer = new DiscriminatedWrapperSerializer(_discriminatorConvention, this); serializer.Serialize(context, value); } } } // protected methods /// /// Adds the item. /// /// The accumulator. /// The item. protected abstract void AddItem(object accumulator, object item); /// /// Creates the accumulator. /// /// The accumulator. protected abstract object CreateAccumulator(); /// /// Enumerates the items in serialization order. /// /// The value. /// The items. protected abstract IEnumerable EnumerateItemsInSerializationOrder(TValue value); /// /// Finalizes the result. /// /// The accumulator. /// The final result. protected abstract TValue FinalizeResult(object accumulator); } /// /// Represents a serializer for enumerable values. /// /// The type of the value. /// The type of the items. public abstract class EnumerableSerializerBase : SerializerBase, IBsonArraySerializer where TValue : class, IEnumerable { // private fields private readonly IDiscriminatorConvention _discriminatorConvention = new ScalarDiscriminatorConvention("_t"); private readonly Lazy> _lazyItemSerializer; // constructors /// /// Initializes a new instance of the class. /// protected EnumerableSerializerBase() : this(BsonSerializer.SerializerRegistry) { } /// /// Initializes a new instance of the class. /// /// The item serializer. protected EnumerableSerializerBase(IBsonSerializer itemSerializer) { if (itemSerializer == null) { throw new ArgumentNullException("itemSerializer"); } _lazyItemSerializer = new Lazy>(() => itemSerializer); } /// /// Initializes a new instance of the class. /// /// The serializer registry. protected EnumerableSerializerBase(IBsonSerializerRegistry serializerRegistry) { if (serializerRegistry == null) { throw new ArgumentNullException("serializerRegistry"); } _lazyItemSerializer = new Lazy>(() => serializerRegistry.GetSerializer()); } // public properties /// /// Gets the item serializer. /// /// /// The item serializer. /// public IBsonSerializer ItemSerializer { get { return _lazyItemSerializer.Value; } } // public methods /// /// Deserializes a value. /// /// The deserialization context. /// The deserialization args. /// A deserialized value. public override TValue Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { var bsonReader = context.Reader; var bsonType = bsonReader.GetCurrentBsonType(); switch (bsonType) { case BsonType.Null: bsonReader.ReadNull(); return null; case BsonType.Array: bsonReader.ReadStartArray(); var accumulator = CreateAccumulator(); while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) { var item = _lazyItemSerializer.Value.Deserialize(context); AddItem(accumulator, item); } bsonReader.ReadEndArray(); return FinalizeResult(accumulator); case BsonType.Document: var serializer = new DiscriminatedWrapperSerializer(_discriminatorConvention, this); if (serializer.IsPositionedAtDiscriminatedWrapper(context)) { return (TValue)serializer.Deserialize(context); } else { goto default; } default: throw CreateCannotDeserializeFromBsonTypeException(bsonType); } } /// /// Tries to get the serialization info for the individual items of the array. /// /// The serialization information. /// /// The serialization info for the items. /// public bool TryGetItemSerializationInfo(out BsonSerializationInfo serializationInfo) { var serializer = _lazyItemSerializer.Value; serializationInfo = new BsonSerializationInfo(null, serializer, serializer.ValueType); return true; } /// /// Serializes a value. /// /// The serialization context. /// The serialization args. /// The object. public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TValue value) { var bsonWriter = context.Writer; if (value == null) { bsonWriter.WriteNull(); } else { var actualType = value.GetType(); if (actualType == args.NominalType) { bsonWriter.WriteStartArray(); foreach (var item in EnumerateItemsInSerializationOrder(value)) { _lazyItemSerializer.Value.Serialize(context, item); } bsonWriter.WriteEndArray(); } else { var serializer = new DiscriminatedWrapperSerializer(_discriminatorConvention, this); serializer.Serialize(context, value); } } } // protected methods /// /// Adds the item. /// /// The accumulator. /// The item. protected abstract void AddItem(object accumulator, TItem item); /// /// Creates the accumulator. /// /// The accumulator. protected abstract object CreateAccumulator(); /// /// Enumerates the items in serialization order. /// /// The value. /// The items. protected abstract IEnumerable EnumerateItemsInSerializationOrder(TValue value); /// /// Finalizes the result. /// /// The accumulator. /// The result. protected abstract TValue FinalizeResult(object accumulator); } }