/* 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.Collections.Generic; using MongoDB.Bson.IO; namespace MongoDB.Bson.Serialization.Serializers { /// /// Represents a serializer for a BsonValue that can round trip C# null. /// /// The type of the BsonValue. public class BsonValueCSharpNullSerializer : SerializerBase where TBsonValue : BsonValue { // private fields private readonly IBsonSerializer _wrappedSerializer; // constructors /// /// Initializes a new instance of the class. /// /// The wrapped serializer. public BsonValueCSharpNullSerializer(IBsonSerializer wrappedSerializer) { _wrappedSerializer = wrappedSerializer; } // public methods /// /// Deserializes a value. /// /// The deserialization context. /// The deserialization args. /// A deserialized value. public override TBsonValue Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { var bsonReader = context.Reader; var bsonType = bsonReader.GetCurrentBsonType(); if (bsonType == BsonType.Document && IsCSharpNullRepresentation(bsonReader)) { // if IsCSharpNullRepresentation returns true it will have consumed the document representing C# null return null; } // handle BSON null for backward compatibility with existing data (new data would have _csharpnull) if (bsonType == BsonType.Null && (args.NominalType != typeof(BsonValue) && args.NominalType != typeof(BsonNull))) { bsonReader.ReadNull(); return null; } return _wrappedSerializer.Deserialize(context); } /// /// Serializes a value. /// /// The serialization context. /// The serialization args. /// The object. public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TBsonValue value) { if (value == null) { var bsonWriter = context.Writer; bsonWriter.WriteStartDocument(); bsonWriter.WriteBoolean("_csharpnull", true); bsonWriter.WriteEndDocument(); } else { _wrappedSerializer.Serialize(context, value); } } // private methods private bool IsCSharpNullRepresentation(IBsonReader bsonReader) { var bookmark = bsonReader.GetBookmark(); bsonReader.ReadStartDocument(); var bsonType = bsonReader.ReadBsonType(); if (bsonType == BsonType.Boolean) { var name = bsonReader.ReadName(); if (name == "_csharpnull" || name == "$csharpnull") { var value = bsonReader.ReadBoolean(); if (value) { bsonType = bsonReader.ReadBsonType(); if (bsonType == BsonType.EndOfDocument) { bsonReader.ReadEndDocument(); return true; } } } } bsonReader.ReturnToBookmark(bookmark); return false; } } /// /// Represents a serializer for a BsonValue that can round trip C# null and implements IBsonArraySerializer and IBsonDocumentSerializer. /// /// The type of the bson value. public class BsonValueCSharpNullArrayAndDocumentSerializer : BsonValueCSharpNullSerializer, IBsonArraySerializer, IBsonDocumentSerializer where TBsonValue : BsonValue { // constructors /// /// Initializes a new instance of the class. /// /// The wrapped serializer. public BsonValueCSharpNullArrayAndDocumentSerializer(IBsonSerializer wrappedSerializer) : base(wrappedSerializer) { } /// /// 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) { return BsonValueSerializer.Instance.TryGetItemSerializationInfo(out serializationInfo); } /// /// Tries to get the serialization info for a member. /// /// Name of the member. /// The serialization information. /// /// true if the serialization info exists; otherwise false. /// public bool TryGetMemberSerializationInfo(string memberName, out BsonSerializationInfo serializationInfo) { return BsonValueSerializer.Instance.TryGetMemberSerializationInfo(memberName, out serializationInfo); } } /// /// Represents a serializer for a BsonValue that can round trip C# null and implements IBsonArraySerializer. /// /// The type of the bson value. public class BsonValueCSharpNullArraySerializer : BsonValueCSharpNullSerializer, IBsonArraySerializer where TBsonValue : BsonValue { // constructors /// /// Initializes a new instance of the class. /// /// The wrapped serializer. public BsonValueCSharpNullArraySerializer(IBsonSerializer wrappedSerializer) : base(wrappedSerializer) { } /// /// 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) { return BsonValueSerializer.Instance.TryGetItemSerializationInfo(out serializationInfo); } } /// /// Represents a serializer for a BsonValue that can round trip C# null and implements IBsonDocumentSerializer. /// /// The type of the bson value. public class BsonValueCSharpNullDocumentSerializer : BsonValueCSharpNullSerializer, IBsonDocumentSerializer where TBsonValue : BsonValue { // constructors /// /// Initializes a new instance of the class. /// /// The wrapped serializer. public BsonValueCSharpNullDocumentSerializer(IBsonSerializer wrappedSerializer) : base(wrappedSerializer) { } /// /// Tries to get the serialization info for a member. /// /// Name of the member. /// The serialization information. /// /// true if the serialization info exists; otherwise false. /// public bool TryGetMemberSerializationInfo(string memberName, out BsonSerializationInfo serializationInfo) { return BsonValueSerializer.Instance.TryGetMemberSerializationInfo(memberName, out serializationInfo); } } }