/* 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 MongoDB.Bson.IO;
using MongoDB.Bson.Serialization.Conventions;
namespace MongoDB.Bson.Serialization.Serializers
{
///
/// Represents a serializer that serializes values as a discriminator/value pair.
///
/// The type of the value.
public class DiscriminatedWrapperSerializer : SerializerBase
{
// private constants
private static class Flags
{
public const long Discriminator = 1;
public const long Value = 2;
public const long Other = 4;
}
// private fields
private readonly IDiscriminatorConvention _discriminatorConvention;
private readonly SerializerHelper _helper;
private readonly SerializerHelper _isPositionedHelper;
private readonly IBsonSerializer _wrappedSerializer;
// constructors
///
/// Initializes a new instance of the class.
///
/// The discriminator convention.
/// The wrapped serializer.
public DiscriminatedWrapperSerializer(IDiscriminatorConvention discriminatorConvention, IBsonSerializer wrappedSerializer)
{
_discriminatorConvention = discriminatorConvention;
_wrappedSerializer = wrappedSerializer;
_helper = new SerializerHelper
(
new SerializerHelper.Member(discriminatorConvention.ElementName, Flags.Discriminator),
new SerializerHelper.Member("_v", Flags.Value)
);
_isPositionedHelper = new SerializerHelper
(
new SerializerHelper.Member(discriminatorConvention.ElementName, Flags.Discriminator, isOptional: true),
new SerializerHelper.Member("_v", Flags.Value, isOptional: true),
new SerializerHelper.Member("*", Flags.Other, isOptional: true)
);
}
// 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 nominalType = args.NominalType;
var actualType = _discriminatorConvention.GetActualType(bsonReader, nominalType);
var serializer = BsonSerializer.LookupSerializer(actualType);
TValue value = default(TValue);
_helper.DeserializeMembers(context, (elementName, flag) =>
{
switch (flag)
{
case Flags.Discriminator:
bsonReader.SkipValue();
break;
case Flags.Value:
var valueDeserializationArgs = new BsonDeserializationArgs { NominalType = actualType };
value = (TValue)serializer.Deserialize(context, valueDeserializationArgs);
break;
}
});
return value;
}
///
/// Determines whether the reader is positioned at a discriminated wrapper.
///
/// The context.
/// True if the reader is positioned at a discriminated wrapper.
public bool IsPositionedAtDiscriminatedWrapper(BsonDeserializationContext context)
{
var bsonReader = context.Reader;
var bookmark = bsonReader.GetBookmark();
try
{
if (bsonReader.GetCurrentBsonType() != BsonType.Document) { return false; }
var foundFields = _isPositionedHelper.DeserializeMembers(context, (elementName, flag) =>
{
context.Reader.SkipValue();
});
return foundFields == (Flags.Discriminator | Flags.Value);
}
finally
{
bsonReader.ReturnToBookmark(bookmark);
}
}
///
/// Serializes a value.
///
/// The serialization context.
/// The serialization args.
/// The value.
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TValue value)
{
var bsonWriter = context.Writer;
var nominalType = args.NominalType;
var actualType = value.GetType();
var discriminator = _discriminatorConvention.GetDiscriminator(nominalType, actualType);
bsonWriter.WriteStartDocument();
bsonWriter.WriteName(_discriminatorConvention.ElementName);
BsonValueSerializer.Instance.Serialize(context, discriminator);
bsonWriter.WriteName("_v");
args.NominalType = actualType;
_wrappedSerializer.Serialize(context, args, value);
bsonWriter.WriteEndDocument();
}
}
}