Details
-
Bug
-
Status: Closed
-
Major - P3
-
Resolution: Works as Designed
-
2.9.3
-
None
-
None
Description
Below is a repro (in an xUnit test method) of a case where a custom serializer is being registered properly and used for serialization, but isn't getting used for deserialization:
namespace Test |
{
|
using System; |
using System.Collections.Generic; |
using System.Collections.ObjectModel; |
using System.Linq; |
using MongoDB.Bson; |
using MongoDB.Bson.IO; |
using MongoDB.Bson.Serialization; |
using MongoDB.Bson.Serialization.Options; |
using MongoDB.Bson.Serialization.Serializers; |
using OBeautifulCode.Serialization.Bson; |
using Xunit; |
|
public static class CustomSerializerTest |
{
|
[Fact]
|
public static void Test() |
{
|
// setup the serializer |
var classMap = new BsonClassMap<MyTestModel>(); |
var memberInfo = typeof(MyTestModel).GetMember(nameof(MyTestModel.MyProperty)).Single(); |
var memberMap = classMap.MapMember(memberInfo); |
var keySerializer = new MyDateTimeSerializer(); |
var valueSerializer = new StringSerializer(); |
var dictionarySerializer = |
new MyDictionarySerializer<IReadOnlyDictionary<DateTime, string>, DateTime, string>( |
DictionaryRepresentation.ArrayOfDocuments, keySerializer, valueSerializer);
|
var serializer = |
new MyListSerializer<IReadOnlyList<IReadOnlyDictionary<DateTime, string>>, |
IReadOnlyDictionary<DateTime, string>>(dictionarySerializer); |
memberMap.SetSerializer(serializer);
|
BsonClassMap.RegisterClassMap(classMap);
|
var expected = new MyTestModel |
{
|
MyProperty =
|
new List<IReadOnlyDictionary<DateTime, string>> |
{
|
new Dictionary<DateTime, string> |
{
|
{DateTime.Now, "whatever"}, |
},
|
},
|
};
|
|
// serialize |
var document = new BsonDocument(); |
using (var writer = new BsonDocumentWriter(document)) |
{
|
BsonSerializer.Serialize(writer, expected.GetType(), expected);
|
writer.Close();
|
}
|
|
// prove that our serializers are being called |
// you can also put breakpoints in all the Serialize methods and run in the debugger |
// all the breakpoints will be hit. |
var actualJson = document.ToJson(); |
var expectedJson = |
"{ \"_t\" : \"MyTestModel\", \"MyProperty\" : { \"_t\" : \"ReadOnlyCollection`1\", \"_v\" : [[{ \"k\" : \"does-not-matter\", \"v\" : \"whatever\" }]] } }"; |
Assert.Equal(expectedJson, actualJson);
|
|
// de-serialize. throws FormatException ("String was not recognized as a valid DateTime") |
// proves that MyDateTimeSerializer is NOT being called. you can also put breakpoints |
// in all of the Deserialize methods and run in debugger - it hit MyListSerializer |
// but doesn't hit MyDictionarySerializer |
ObcBsonSerializerHelper.DeserializeFromDocument<MyTestModel>(document);
|
}
|
|
private class MyTestModel |
{
|
public IReadOnlyList<IReadOnlyDictionary<DateTime, string>> MyProperty { get; set; } |
}
|
|
private class MyListSerializer<TCollection, TElement> : SerializerBase<TCollection> |
where TCollection : class, IEnumerable<TElement> |
{
|
private readonly ReadOnlyCollectionSerializer<TElement> underlyingSerializer; |
|
public MyListSerializer(IBsonSerializer<TElement> elementSerializer) |
{
|
this.underlyingSerializer = new ReadOnlyCollectionSerializer<TElement>(elementSerializer); |
}
|
|
/// <inheritdoc /> |
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, |
TCollection value)
|
{
|
if (value == null) |
{
|
context.Writer.WriteNull();
|
return; |
}
|
|
this.underlyingSerializer.Serialize(context, args, new ReadOnlyCollection<TElement>(value.ToList())); |
}
|
|
/// <inheritdoc /> |
public override TCollection Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) |
{
|
if (context.Reader.State != BsonReaderState.Type && context.Reader.CurrentBsonType == BsonType.Null) |
{
|
context.Reader.ReadNull();
|
return null; |
}
|
|
var collection = this.underlyingSerializer.Deserialize(context, args); |
var result = collection.ToList() as TCollection; |
return result; |
}
|
}
|
|
private class MyDictionarySerializer<TDictionary, TKey, TValue> : SerializerBase<TDictionary> |
where TDictionary : class, IEnumerable<KeyValuePair<TKey, TValue>> |
{
|
private readonly DictionaryInterfaceImplementerSerializer<Dictionary<TKey, TValue>> underlyingSerializer; |
|
public MyDictionarySerializer(DictionaryRepresentation dictionaryRepresentation, |
IBsonSerializer keySerializer, IBsonSerializer valueSerializer)
|
{
|
this.underlyingSerializer = |
new DictionaryInterfaceImplementerSerializer<Dictionary<TKey, TValue>>(dictionaryRepresentation, |
keySerializer, valueSerializer);
|
}
|
|
/// <inheritdoc /> |
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, |
TDictionary value)
|
{
|
if (value == null) |
{
|
context.Writer.WriteNull();
|
return; |
}
|
|
this.underlyingSerializer.Serialize(context, args, |
((IDictionary<TKey, TValue>) value).ToDictionary(_ => _.Key, _ => _.Value));
|
}
|
|
/// <inheritdoc /> |
public override TDictionary Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) |
{
|
if ((context.Reader.State != BsonReaderState.Type) && (context.Reader.CurrentBsonType == BsonType.Null)) |
{
|
context.Reader.ReadNull();
|
return null; |
}
|
|
var dictionary = this.underlyingSerializer.Deserialize(context, args); |
var result = new ReadOnlyDictionary<TKey, TValue>(dictionary) as TDictionary; |
return result; |
}
|
}
|
|
private class MyDateTimeSerializer : SerializerBase<DateTime> |
{
|
private const string DoesNotMatter = "does-not-matter"; |
|
/// <inheritdoc /> |
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, DateTime value) |
{
|
context.Writer.WriteString(DoesNotMatter);
|
}
|
|
/// <inheritdoc /> |
public override DateTime Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) |
{
|
var type = context.Reader.GetCurrentBsonType(); |
if ((type == BsonType.String) && (context.Reader.ReadString() == DoesNotMatter)) |
{
|
return DateTime.Now; |
}
|
|
throw new NotSupportedException(); |
}
|
}
|
}
|
}
|
Attachments
Issue Links
- related to
-
CSHARP-2877 Default BsonSerializationArgs sometimes has null NominalType
-
- Closed
-