/* 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);
}
}
}