Uploaded image for project: 'C# Driver'
  1. C# Driver
  2. CSHARP-2860

custom serializer not being called

    • Type: Icon: Bug Bug
    • Resolution: Works as Designed
    • Priority: Icon: Major - P3 Major - P3
    • None
    • Affects Version/s: 2.9.3
    • Component/s: BSON
    • None

      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:

      Unable to find source-code formatter for language: csharp. Available languages are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, yaml
      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();
                  }
              }
          }
      }
      

            Assignee:
            mikalai.mazurenka@mongodb.com Mikalai Mazurenka (Inactive)
            Reporter:
            suraj@cometrics.com Suraj Gupta
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: