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();
|
}
|
}
|
}
|
}
|