Skip to content

Commit fd47b44

Browse files
author
rstam
committed
CSHARP-932: Fix queries comparing BsonValue properties to C# null.
1 parent 6352f0e commit fd47b44

File tree

6 files changed

+638
-48
lines changed

6 files changed

+638
-48
lines changed

MongoDB.Bson/MongoDB.Bson.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@
196196
<Compile Include="Serialization\Serializers\BsonArraySerializer.cs" />
197197
<Compile Include="Serialization\Serializers\BsonBinaryDataSerializer.cs" />
198198
<Compile Include="Serialization\Serializers\BsonBooleanSerializer.cs" />
199+
<Compile Include="Serialization\Serializers\BsonValueCSharpNullSerializer.cs" />
199200
<Compile Include="Serialization\Serializers\BsonDateTimeSerializer.cs" />
200201
<Compile Include="Serialization\Serializers\BsonDocumentBackedClassSerializer.cs" />
201202
<Compile Include="Serialization\Attributes\IBsonClassMapModifier.cs" />

MongoDB.Bson/Serialization/BsonMemberMap.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
using System.Reflection.Emit;
2020
using MongoDB.Bson.Serialization.Conventions;
2121
using MongoDB.Bson.Serialization.Options;
22+
using MongoDB.Bson.Serialization.Serializers;
2223

2324
namespace MongoDB.Bson.Serialization
2425
{
@@ -288,6 +289,12 @@ public IBsonSerializer GetSerializer(Type actualType)
288289
}
289290
else
290291
{
292+
// return special serializer for BsonValue members that handles the _csharpnull representation
293+
if (_memberTypeIsBsonValue)
294+
{
295+
return BsonValueCSharpNullSerializer.Instance;
296+
}
297+
291298
// return a cached serializer when possible
292299
if (actualType == _memberType)
293300
{

MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -563,21 +563,6 @@ private object DeserializeMemberValue(BsonReader bsonReader, BsonMemberMap membe
563563
bsonReader.ReadNull();
564564
return null;
565565
}
566-
else if (memberMap.MemberTypeIsBsonValue)
567-
{
568-
if (bsonType == BsonType.Document && IsCSharpNullRepresentation(bsonReader))
569-
{
570-
// if IsCSharpNullRepresentation returns true it will have consumed the document representing C# null
571-
return null;
572-
}
573-
574-
// handle BSON null for backward compatibility with existing data (new data would have _csharpnull)
575-
if (bsonType == BsonType.Null && (nominalType != typeof(BsonValue) && nominalType != typeof(BsonNull)))
576-
{
577-
bsonReader.ReadNull();
578-
return null;
579-
}
580-
}
581566

582567
Type actualType;
583568
if (bsonType == BsonType.Null)
@@ -602,33 +587,6 @@ private object DeserializeMemberValue(BsonReader bsonReader, BsonMemberMap membe
602587
}
603588
}
604589

605-
private bool IsCSharpNullRepresentation(BsonReader bsonReader)
606-
{
607-
var bookmark = bsonReader.GetBookmark();
608-
bsonReader.ReadStartDocument();
609-
var bsonType = bsonReader.ReadBsonType();
610-
if (bsonType == BsonType.Boolean)
611-
{
612-
var name = bsonReader.ReadName();
613-
if (name == "_csharpnull" || name == "$csharpnull")
614-
{
615-
var value = bsonReader.ReadBoolean();
616-
if (value)
617-
{
618-
bsonType = bsonReader.ReadBsonType();
619-
if (bsonType == BsonType.EndOfDocument)
620-
{
621-
bsonReader.ReadEndDocument();
622-
return true;
623-
}
624-
}
625-
}
626-
}
627-
628-
bsonReader.ReturnToBookmark(bookmark);
629-
return false;
630-
}
631-
632590
private void SerializeExtraElements(BsonWriter bsonWriter, object obj, BsonMemberMap extraElementsMemberMap)
633591
{
634592
var extraElements = extraElementsMemberMap.Getter(obj);
@@ -679,12 +637,6 @@ private void SerializeMember(BsonWriter bsonWriter, object obj, BsonMemberMap me
679637
{
680638
bsonWriter.WriteNull();
681639
}
682-
else if (value == null && memberMap.MemberTypeIsBsonValue)
683-
{
684-
bsonWriter.WriteStartDocument();
685-
bsonWriter.WriteBoolean("_csharpnull", true);
686-
bsonWriter.WriteEndDocument();
687-
}
688640
else
689641
{
690642
var actualType = (value == null) ? nominalType : value.GetType();
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/* Copyright 2010-2014 MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System;
17+
using System.IO;
18+
using MongoDB.Bson.IO;
19+
20+
namespace MongoDB.Bson.Serialization.Serializers
21+
{
22+
/// <summary>
23+
/// Represents a serializer for BsonValue members of a BsonClassMap.
24+
/// </summary>
25+
public class BsonValueCSharpNullSerializer : BsonBaseSerializer, IBsonArraySerializer, IBsonDocumentSerializer
26+
{
27+
// private fields
28+
private static BsonValueCSharpNullSerializer __instance = new BsonValueCSharpNullSerializer();
29+
30+
// public static properties
31+
/// <summary>
32+
/// Gets an instance of the BsonBooleanSerializer class.
33+
/// </summary>
34+
public static BsonValueCSharpNullSerializer Instance
35+
{
36+
get { return __instance; }
37+
}
38+
39+
// public methods
40+
/// <summary>
41+
/// Deserializes an object from a BsonReader.
42+
/// </summary>
43+
/// <param name="bsonReader">The BsonReader.</param>
44+
/// <param name="nominalType">The nominal type of the object.</param>
45+
/// <param name="actualType">The actual type of the object.</param>
46+
/// <param name="options">The serialization options.</param>
47+
/// <returns>An object.</returns>
48+
public override object Deserialize(
49+
BsonReader bsonReader,
50+
Type nominalType,
51+
Type actualType,
52+
IBsonSerializationOptions options)
53+
{
54+
var bsonType = bsonReader.GetCurrentBsonType();
55+
if (bsonType == BsonType.Document && IsCSharpNullRepresentation(bsonReader))
56+
{
57+
// if IsCSharpNullRepresentation returns true it will have consumed the document representing C# null
58+
return null;
59+
}
60+
61+
// handle BSON null for backward compatibility with existing data (new data would have _csharpnull)
62+
if (bsonType == BsonType.Null && (nominalType != typeof(BsonValue) && nominalType != typeof(BsonNull)))
63+
{
64+
bsonReader.ReadNull();
65+
return null;
66+
}
67+
68+
var serializer = BsonSerializer.LookupSerializer(actualType);
69+
return serializer.Deserialize(bsonReader, nominalType, actualType, options);
70+
}
71+
72+
/// <summary>
73+
/// Gets the serialization info for a member.
74+
/// </summary>
75+
/// <param name="memberName">The member name.</param>
76+
/// <returns>
77+
/// The serialization info for the member.
78+
/// </returns>
79+
public BsonSerializationInfo GetMemberSerializationInfo(string memberName)
80+
{
81+
return BsonValueSerializer.Instance.GetMemberSerializationInfo(memberName);
82+
}
83+
84+
/// <summary>
85+
/// Gets the serialization info for individual items of the array.
86+
/// </summary>
87+
/// <returns>
88+
/// The serialization info for the items.
89+
/// </returns>
90+
public BsonSerializationInfo GetItemSerializationInfo()
91+
{
92+
return BsonValueSerializer.Instance.GetItemSerializationInfo();
93+
}
94+
95+
/// <summary>
96+
/// Serializes an object to a BsonWriter.
97+
/// </summary>
98+
/// <param name="bsonWriter">The BsonWriter.</param>
99+
/// <param name="nominalType">The nominal type.</param>
100+
/// <param name="value">The object.</param>
101+
/// <param name="options">The serialization options.</param>
102+
public override void Serialize(
103+
BsonWriter bsonWriter,
104+
Type nominalType,
105+
object value,
106+
IBsonSerializationOptions options)
107+
{
108+
if (value == null)
109+
{
110+
bsonWriter.WriteStartDocument();
111+
bsonWriter.WriteBoolean("_csharpnull", true);
112+
bsonWriter.WriteEndDocument();
113+
}
114+
else
115+
{
116+
BsonValueSerializer.Instance.Serialize(bsonWriter, nominalType, value, options);
117+
}
118+
}
119+
120+
// private methods
121+
private bool IsCSharpNullRepresentation(BsonReader bsonReader)
122+
{
123+
var bookmark = bsonReader.GetBookmark();
124+
bsonReader.ReadStartDocument();
125+
var bsonType = bsonReader.ReadBsonType();
126+
if (bsonType == BsonType.Boolean)
127+
{
128+
var name = bsonReader.ReadName();
129+
if (name == "_csharpnull" || name == "$csharpnull")
130+
{
131+
var value = bsonReader.ReadBoolean();
132+
if (value)
133+
{
134+
bsonType = bsonReader.ReadBsonType();
135+
if (bsonType == BsonType.EndOfDocument)
136+
{
137+
bsonReader.ReadEndDocument();
138+
return true;
139+
}
140+
}
141+
}
142+
}
143+
144+
bsonReader.ReturnToBookmark(bookmark);
145+
return false;
146+
}
147+
}
148+
}

0 commit comments

Comments
 (0)