0

I have the following dictionary:

var dict = new Dictionary<string, object> {
  { "decimal", 3.503m },
  { "int", 45 }
};
var serializedString = dict.ToJson();

By default that is serialized as:

{ "decimal" : { "_t" : "System.Decimal", "_v" : "3.503" }, "int" : 45 } 

If I override DecimalSerializer as:

BsonSerializer.RegisterSerializer<decimal>(new DecimalSerializer().WithRepresentation(BsonType.Double));

That only influences on how "_v" value is serialized, e.g.:

{ "decimal" : { "_t" : "System.Decimal", "_v" : 3.503 }, "int" : 45 } 

Expected result:

{ "decimal" : 3.503, "int" : 45 } 

Please advise

lekrus
  • 41
  • 3
  • Try this link. I think it can help you. [How to use decimal type in MongoDB](https://stackoverflow.com/questions/43473147/how-to-use-decimal-type-in-mongodb) – Mohammad Taherian Apr 25 '19 at 13:22

2 Answers2

0

The cause of the .Net types in the bson, is the lack of type in the dictionary. The Bson serializers are trying to get enough state to restore the original object of the items in the dictionary. From the context (the dictionary) they are of type "object", so the .Net type is inserted to know enough when deserializing.

The following solutions answer your question but lose the type information for deserializing.

Solution 1: Change the dictionary type to <string, decimal>

var dict = new Dictionary<string, decimal> {
  { "decimal", 3.503m },
  { "int", 45 }
};
var serializedString = dict.ToJson();

Results in: { "decimal" : "3.503", "int" : "45" }

With your override of the decimal serializer, you get the expected result.

{ "decimal" : 3.503, "int" : 45 }

Solution 2: Change the dictionary type to <string, double>

var dict = new Dictionary<string, double> {
  { "decimal", (double)3.503m },
  { "int", 45 }
};
var serializedString = dict.ToJson();

Results in the expected result: { "decimal" : 3.503, "int" : 45 }

Solution 3: Use custom serializer

public class MyDictionarySerializer : SerializerBase<Dictionary<string, object>>
{
  public override void Serialize(MongoDB.Bson.Serialization.BsonSerializationContext context, MongoDB.Bson.Serialization.BsonSerializationArgs args, Dictionary<string, object> dictionary)
  {
    context.Writer.WriteStartArray();
    foreach (var item in dictionary)
    {
      context.Writer.WriteStartDocument();
      context.Writer.WriteString(item.Key);

      // TODO your converstions from object to double
      var value = (double)item.Value;

      context.Writer.WriteDouble(value);
      context.Writer.WriteEndDocument();
    }
    context.Writer.WriteEndArray();
  }

  public override Dictionary<string, object> Deserialize(MongoDB.Bson.Serialization.BsonDeserializationContext context, MongoDB.Bson.Serialization.BsonDeserializationArgs args)
  {
    context.Reader.ReadStartArray();

    var result = new Dictionary<string, object>();
    while (true)
    {
      try
      {
        //this catch block only need to identify the end of the Array
        context.Reader.ReadStartDocument();
      }
      catch (Exception exp)
      {
        context.Reader.ReadEndArray();
        break;
      }

      var key = context.Reader.ReadString();
      double value = context.Reader.ReadDouble();
      result.Add(key, value);

      context.Reader.ReadEndDocument();
    }
    return result;
  }
}
Lakerfield
  • 1,071
  • 1
  • 7
  • 19
  • Thank you for suggested options. Yes, I understand I will lose type information and that is what I want to achieve. I need decimals to be treated the same way as doubles (and deserialized as doubles). In addition to suggested options, I guess I can also just iterate my dictionary and cast all decimal values to doubles first and then call the default serializer, that will produce required result. But as soon as serializer iterates over the same dictionary during its execution, I thought there is more standard way of overriding that behaviour and prevent pre-casting of values in my dictionary? – lekrus Apr 07 '16 at 15:25
0

As another option, it's possible to override object serializer

public class DecimalsOverridingObjectSerializer : ObjectSerializer
{
    public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) {
        if (value != null && value is decimal) {
            base.Serialize(context, args, Convert.ToDouble(value));
        } else {
            base.Serialize(context, args, value);
        }
    }
}
BsonSerializer.RegisterSerializer(typeof(object), new DecimalsOverridingObjectSerializer());

that still will not work for Hashtables. Possible workaround for Hashtables:

public class DecimalsOverridingDictionarySerializer<TDictionary>: 
    DictionaryInterfaceImplementerSerializer<TDictionary>
    where TDictionary : class, IDictionary, new()
{
    public DecimalsOverridingDictionarySerializer(DictionaryRepresentation dictionaryRepresentation)
           : base(dictionaryRepresentation, new DecimalsOverridingObjectSerializer(), new DecimalsOverridingObjectSerializer())
    {   }

}
BsonSerializer.RegisterSerializer(typeof(Hashtable), new DecimalsOverridingDictionarySerializer<Hashtable>(DictionaryRepresentation.Document));
lekrus
  • 41
  • 3