Skip to content

Commit 3ef4505

Browse files
authored
CSHARP-4205: Fix serialization of generic enumerable not implementing List<T> (#819)
1 parent 36e6228 commit 3ef4505

File tree

2 files changed

+111
-7
lines changed

2 files changed

+111
-7
lines changed

src/MongoDB.Bson/Serialization/CollectionsSerializationProvider.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -224,14 +224,15 @@ private IBsonSerializer GetCollectionSerializer(Type type, IBsonSerializerRegist
224224
{
225225
var listDefinition = typeof(List<>);
226226
var listType = listDefinition.MakeGenericType(itemType);
227-
var serializerDefinition = typeof(ImpliedImplementationInterfaceSerializer<,>);
228-
return CreateGenericSerializer(serializerDefinition, new[] { type, listType }, serializerRegistry);
229-
}
230-
else
231-
{
232-
var serializerDefinition = typeof(EnumerableInterfaceImplementerSerializer<,>);
233-
return CreateGenericSerializer(serializerDefinition, new[] { type, itemType }, serializerRegistry);
227+
if (typeInfo.IsAssignableFrom(listType))
228+
{
229+
var serializerDefinition = typeof(ImpliedImplementationInterfaceSerializer<,>);
230+
return CreateGenericSerializer(serializerDefinition, new[] { type, listType }, serializerRegistry);
231+
}
234232
}
233+
234+
var enumerableSerializerDefinition = typeof(EnumerableInterfaceImplementerSerializer<,>);
235+
return CreateGenericSerializer(enumerableSerializerDefinition, new[] { type, itemType }, serializerRegistry);
235236
}
236237
else if (implementedEnumerableInterface != null)
237238
{

tests/MongoDB.Bson.Tests/Serialization/Serializers/GenericEnumerableSerializerTests.cs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* limitations under the License.
1414
*/
1515

16+
using System.Collections;
1617
using System.Collections.Generic;
1718
using System.Linq;
1819
using MongoDB.Bson;
@@ -207,4 +208,106 @@ public void TestTwoAddresses()
207208
Assert.True(bson.SequenceEqual(rehydrated.ToBson()));
208209
}
209210
}
211+
212+
public class ICustomCollectionSerializerTests
213+
{
214+
public interface IAddressCollection : ICollection<Address>
215+
{
216+
}
217+
218+
public class AddressCollection : IAddressCollection
219+
{
220+
private readonly List<Address> data = new();
221+
public int Count => data.Count;
222+
public bool IsReadOnly => false;
223+
public void Add(Address item) => data.Add(item);
224+
public void Clear() => data.Clear();
225+
public bool Contains(Address item) => data.Contains(item);
226+
public void CopyTo(Address[] array, int arrayIndex) => data.CopyTo(array, arrayIndex);
227+
public IEnumerator<Address> GetEnumerator() => data.GetEnumerator();
228+
public bool Remove(Address item) => data.Remove(item);
229+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
230+
}
231+
232+
public class TestClass
233+
{
234+
public IAddressCollection Addresses { get; set; }
235+
}
236+
237+
[Fact]
238+
public void TestNull()
239+
{
240+
var obj = new TestClass { Addresses = null };
241+
var json = obj.ToJson();
242+
var expected = "{ 'Addresses' : null }".Replace("'", "\"");
243+
Assert.Equal(expected, json);
244+
245+
var bson = obj.ToBson();
246+
var rehydrated = BsonSerializer.Deserialize<TestClass>(bson);
247+
Assert.True(bson.SequenceEqual(rehydrated.ToBson()));
248+
}
249+
250+
[Fact]
251+
public void TestEmpty()
252+
{
253+
var obj = new TestClass
254+
{
255+
Addresses = new AddressCollection()
256+
};
257+
var json = obj.ToJson();
258+
var expected = "{ 'Addresses' : { '_t' : 'AddressCollection', '_v' : [] } }".Replace("'", "\"");
259+
Assert.Equal(expected, json);
260+
261+
var bson = obj.ToBson();
262+
var rehydrated = BsonSerializer.Deserialize<TestClass>(bson);
263+
Assert.IsType<AddressCollection>(rehydrated.Addresses);
264+
Assert.True(bson.SequenceEqual(rehydrated.ToBson()));
265+
}
266+
267+
[Fact]
268+
public void TestOneAddress()
269+
{
270+
var obj = new TestClass
271+
{
272+
Addresses = new AddressCollection
273+
{
274+
new Address { Street = "123 Main", City = "Smithtown", State = "PA", Zip = 12345 }
275+
}
276+
};
277+
var json = obj.ToJson();
278+
var expected = "{ 'Addresses' : { '_t' : 'AddressCollection', '_v' : [#A1] } }";
279+
expected = expected.Replace("#A1", "{ 'Street' : '123 Main', 'City' : 'Smithtown', 'State' : 'PA', 'Zip' : 12345 }");
280+
expected = expected.Replace("'", "\"");
281+
Assert.Equal(expected, json);
282+
283+
var bson = obj.ToBson();
284+
var rehydrated = BsonSerializer.Deserialize<TestClass>(bson);
285+
Assert.IsType<AddressCollection>(rehydrated.Addresses);
286+
Assert.True(bson.SequenceEqual(rehydrated.ToBson()));
287+
}
288+
289+
[Fact]
290+
public void TestTwoAddresses()
291+
{
292+
var obj = new TestClass
293+
{
294+
Addresses = new AddressCollection
295+
{
296+
new Address { Street = "123 Main", City = "Smithtown", State = "PA", Zip = 12345 },
297+
new Address { Street = "456 First", City = "Johnstown", State = "MD", Zip = 45678 }
298+
}
299+
};
300+
var json = obj.ToJson();
301+
var expected = "{ 'Addresses' : { '_t' : 'AddressCollection', '_v' : [#A1, #A2] } }";
302+
expected = expected.Replace("#A1", "{ 'Street' : '123 Main', 'City' : 'Smithtown', 'State' : 'PA', 'Zip' : 12345 }");
303+
expected = expected.Replace("#A2", "{ 'Street' : '456 First', 'City' : 'Johnstown', 'State' : 'MD', 'Zip' : 45678 }");
304+
expected = expected.Replace("'", "\"");
305+
Assert.Equal(expected, json);
306+
307+
var bson = obj.ToBson();
308+
var rehydrated = BsonSerializer.Deserialize<TestClass>(bson);
309+
Assert.IsType<AddressCollection>(rehydrated.Addresses);
310+
Assert.True(bson.SequenceEqual(rehydrated.ToBson()));
311+
}
312+
}
210313
}

0 commit comments

Comments
 (0)