Skip to content

Commit de62d01

Browse files
committed
CSHARP-1351: Deserializing discriminated objects should not be dependent on the order of the _t and _v elements.
1 parent db12f43 commit de62d01

File tree

9 files changed

+123
-47
lines changed

9 files changed

+123
-47
lines changed

MongoDB.Bson/MongoDB.Bson.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@
358358
<Compile Include="Serialization\Serializers\ThreeDimensionalArraySerializer.cs" />
359359
<Compile Include="Serialization\Serializers\TimeSpanSerializer.cs" />
360360
<Compile Include="Serialization\Serializers\TwoDimensionalArraySerializer.cs" />
361+
<Compile Include="Serialization\Serializers\DiscriminatorValuePairDeserializationHelper.cs" />
361362
<Compile Include="Serialization\Serializers\UInt16Serializer.cs" />
362363
<Compile Include="Serialization\Serializers\UInt32Serializer.cs" />
363364
<Compile Include="Serialization\Serializers\UInt64Serializer.cs" />

MongoDB.Bson/Serialization/Serializers/BitmapSerializer.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright 2010-2014 MongoDB Inc.
1+
/* Copyright 2010-2015 MongoDB Inc.
22
*
33
* Licensed under the Apache License, Version 2.0 (the "License");
44
* you may not use this file except in compliance with the License.
@@ -93,11 +93,8 @@ public override object Deserialize(
9393
break;
9494

9595
case BsonType.Document:
96-
bsonReader.ReadStartDocument();
97-
bsonReader.ReadString("_t");
98-
bytes = bsonReader.ReadBytes("bitmap");
99-
bsonReader.ReadEndDocument();
100-
break;
96+
var helper = new DiscriminatorValuePairDeserializationHelper("_t", "bitmap");
97+
return helper.Deserialize(bsonReader, actualType, this, options);
10198

10299
default:
103100
var message = string.Format("BsonType must be Null, Binary or Document, not {0}.", bsonType);

MongoDB.Bson/Serialization/Serializers/DictionaryGenericSerializer.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright 2010-2014 MongoDB Inc.
1+
/* Copyright 2010-2015 MongoDB Inc.
22
*
33
* Licensed under the Apache License, Version 2.0 (the "License");
44
* you may not use this file except in compliance with the License.
@@ -81,12 +81,8 @@ public override object Deserialize(
8181
{
8282
if (nominalType == typeof(object))
8383
{
84-
bsonReader.ReadStartDocument();
85-
bsonReader.ReadString("_t"); // skip over discriminator
86-
bsonReader.ReadName("_v");
87-
var value = Deserialize(bsonReader, actualType, options); // recursive call replacing nominalType with actualType
88-
bsonReader.ReadEndDocument();
89-
return value;
84+
var helper = new DiscriminatorValuePairDeserializationHelper("_t", "_v");
85+
return helper.Deserialize(bsonReader, actualType, this, options);
9086
}
9187

9288
var dictionary = CreateInstance(actualType);

MongoDB.Bson/Serialization/Serializers/DictionarySerializer.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright 2010-2014 MongoDB Inc.
1+
/* Copyright 2010-2015 MongoDB Inc.
22
*
33
* Licensed under the Apache License, Version 2.0 (the "License");
44
* you may not use this file except in compliance with the License.
@@ -94,12 +94,8 @@ public override object Deserialize(
9494
{
9595
if (nominalType == typeof(object))
9696
{
97-
bsonReader.ReadStartDocument();
98-
bsonReader.ReadString("_t"); // skip over discriminator
99-
bsonReader.ReadName("_v");
100-
var value = Deserialize(bsonReader, actualType, options); // recursive call replacing nominalType with actualType
101-
bsonReader.ReadEndDocument();
102-
return value;
97+
var helper = new DiscriminatorValuePairDeserializationHelper("_t", "_v");
98+
return helper.Deserialize(bsonReader, actualType, this, options);
10399
}
104100

105101
var dictionary = CreateInstance(actualType);
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/* Copyright 2015 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 MongoDB.Bson.IO;
18+
19+
namespace MongoDB.Bson.Serialization.Serializers
20+
{
21+
internal class DiscriminatorValuePairDeserializationHelper
22+
{
23+
private readonly string _discriminatorElementName;
24+
private readonly string _valueElementName;
25+
26+
public DiscriminatorValuePairDeserializationHelper(string discriminatorElementName, string valueElementName)
27+
{
28+
_discriminatorElementName = discriminatorElementName;
29+
_valueElementName = valueElementName;
30+
}
31+
32+
public object Deserialize(
33+
BsonReader bsonReader,
34+
Type actualType,
35+
IBsonSerializer valueSerializer,
36+
IBsonSerializationOptions options)
37+
{
38+
object value = null;
39+
var wasValuePresent = false;
40+
41+
bsonReader.ReadStartDocument();
42+
while (bsonReader.ReadBsonType() != 0)
43+
{
44+
var name = bsonReader.ReadName();
45+
if (name == _discriminatorElementName)
46+
{
47+
bsonReader.SkipValue();
48+
}
49+
else if (name == _valueElementName)
50+
{
51+
value = valueSerializer.Deserialize(bsonReader, actualType, actualType, options);
52+
wasValuePresent = true;
53+
}
54+
else
55+
{
56+
var message = string.Format("Unexpected element name: '{0}'", name);
57+
throw new FormatException(message);
58+
}
59+
}
60+
bsonReader.ReadEndDocument();
61+
62+
if (!wasValuePresent)
63+
{
64+
throw new FormatException("_v element missing");
65+
}
66+
67+
return value;
68+
}
69+
}
70+
}

MongoDB.Bson/Serialization/Serializers/EnumerableSerializerBase.cs

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,8 @@ public override object Deserialize(
9191
return FinalizeResult(instance, actualType);
9292

9393
case BsonType.Document:
94-
bsonReader.ReadStartDocument();
95-
bsonReader.ReadString("_t"); // skip over discriminator
96-
bsonReader.ReadName("_v");
97-
var value = Deserialize(bsonReader, actualType, actualType, options);
98-
bsonReader.ReadEndDocument();
99-
return value;
94+
var helper = new DiscriminatorValuePairDeserializationHelper("_t", "_v");
95+
return helper.Deserialize(bsonReader, actualType, this, options);
10096

10197
default:
10298
var message = string.Format("Can't deserialize a {0} from BsonType {1}.", nominalType.FullName, bsonType);
@@ -311,12 +307,8 @@ public override object Deserialize(
311307
return FinalizeResult(instance, actualType);
312308

313309
case BsonType.Document:
314-
bsonReader.ReadStartDocument();
315-
bsonReader.ReadString("_t"); // skip over discriminator
316-
bsonReader.ReadName("_v");
317-
var value = Deserialize(bsonReader, actualType, actualType, options);
318-
bsonReader.ReadEndDocument();
319-
return value;
310+
var helper = new DiscriminatorValuePairDeserializationHelper("_t", "_v");
311+
return helper.Deserialize(bsonReader, actualType, this, options);
320312

321313
default:
322314
var message = string.Format("Can't deserialize a {0} from BsonType {1}.", actualType.FullName, bsonType);

MongoDB.Bson/Serialization/Serializers/ThreeDimensionalArraySerializer.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright 2010-2014 MongoDB Inc.
1+
/* Copyright 2010-2015 MongoDB Inc.
22
*
33
* Licensed under the Apache License, Version 2.0 (the "License");
44
* you may not use this file except in compliance with the License.
@@ -144,12 +144,8 @@ public override object Deserialize(
144144

145145
return array;
146146
case BsonType.Document:
147-
bsonReader.ReadStartDocument();
148-
bsonReader.ReadString("_t"); // skip over discriminator
149-
bsonReader.ReadName("_v");
150-
var value = Deserialize(bsonReader, actualType, actualType, options);
151-
bsonReader.ReadEndDocument();
152-
return value;
147+
var helper = new DiscriminatorValuePairDeserializationHelper("_t", "_v");
148+
return helper.Deserialize(bsonReader, actualType, this, options);
153149
default:
154150
message = string.Format("Can't deserialize a {0} from BsonType {1}.", actualType.FullName, bsonType);
155151
throw new FileFormatException(message);

MongoDB.Bson/Serialization/Serializers/TwoDimensionalArraySerializer.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright 2010-2014 MongoDB Inc.
1+
/* Copyright 2010-2015 MongoDB Inc.
22
*
33
* Licensed under the Apache License, Version 2.0 (the "License");
44
* you may not use this file except in compliance with the License.
@@ -127,12 +127,8 @@ public override object Deserialize(
127127

128128
return array;
129129
case BsonType.Document:
130-
bsonReader.ReadStartDocument();
131-
bsonReader.ReadString("_t"); // skip over discriminator
132-
bsonReader.ReadName("_v");
133-
var value = Deserialize(bsonReader, actualType, actualType, options);
134-
bsonReader.ReadEndDocument();
135-
return value;
130+
var helper = new DiscriminatorValuePairDeserializationHelper("_t", "_v");
131+
return helper.Deserialize(bsonReader, actualType, this, options);
136132
default:
137133
message = string.Format("Can't deserialize a {0} from BsonType {1}.", actualType.FullName, bsonType);
138134
throw new FileFormatException(message);

MongoDB.BsonUnitTests/Serialization/Serializers/ObjectSerializerTests.cs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright 2010-2014 MongoDB Inc.
1+
/* Copyright 2010-2015 MongoDB Inc.
22
*
33
* Licensed under the Apache License, Version 2.0 (the "License");
44
* you may not use this file except in compliance with the License.
@@ -13,6 +13,8 @@
1313
* limitations under the License.
1414
*/
1515

16+
using System;
17+
using System.IO;
1618
using System.Linq;
1719
using MongoDB.Bson;
1820
using MongoDB.Bson.Serialization;
@@ -137,6 +139,18 @@ public void TestInt64()
137139
Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson()));
138140
}
139141

142+
[TestCase("{ Obj : { _v : [] } }")]
143+
public void TestMissingDiscriminator(string json)
144+
{
145+
Assert.Throws<FileFormatException>(() => BsonSerializer.Deserialize<C>(json));
146+
}
147+
148+
[TestCase("{ Obj : { _t : \"System.Object[]\" } }")]
149+
public void TestMissingValue(string json)
150+
{
151+
Assert.Throws<FileFormatException>(() => BsonSerializer.Deserialize<C>(json));
152+
}
153+
140154
[Test]
141155
public void TestNull()
142156
{
@@ -163,6 +177,24 @@ public void TestObject()
163177
Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson()));
164178
}
165179

180+
[TestCase("{ Obj : { _t : \"System.Object[]\", _v : [] } }")]
181+
[TestCase("{ Obj : { _v : [], _t : \"System.Object[]\" } }")]
182+
public void TestOrderOfElementsDoesNotMatter(string json)
183+
{
184+
var result = BsonSerializer.Deserialize<C>(json);
185+
186+
Assert.IsInstanceOf<object[]>(result.Obj);
187+
}
188+
189+
//[TestCase("{ Obj : { _t : \"System.Guid\", _v : \"01020304-0506-0708-090a-0b0c0d0e0f10\" } }", "01020304-0506-0708-090a-0b0c0d0e0f10")]
190+
//[TestCase("{ Obj : { _v : \"01020304-0506-0708-090a-0b0c0d0e0f10\", _t : \"System.Guid\" } }", "01020304-0506-0708-090a-0b0c0d0e0f10")]
191+
//public void TestOrderOfElementsDoesNotMatter(string json, string expectedGuid)
192+
//{
193+
// var result = BsonSerializer.Deserialize<C>(json);
194+
//
195+
// Assert.AreEqual(new Guid(expectedGuid), result.Obj);
196+
//}
197+
166198
[Test]
167199
public void TestString()
168200
{

0 commit comments

Comments
 (0)