如何在mongodb csharp驱动程序中强制将字典内的十进制值序列化为双精度数?

时间:2016-04-05 17:44:54

标签: mongodb-.net-driver mongodb-csharp-2.0

我有以下字典:

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

默认情况下序列化为:

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

如果我将DecimalSerializer覆盖为:

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

这只影响&#34; _v&#34;值是序列化的,例如:

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

预期结果:

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

请告知

2 个答案:

答案 0 :(得分:0)

bson中.Net类型的原因是字典中缺少类型。 Bson序列化程序试图获得足够的状态来恢复字典中项目的原始对象。从上下文(字典)中它们是“对象”类型,因此插入.Net类型以便在反序列化时足够了解。

以下解决方案可以回答您的问题,但会丢失反序列化的类型信息

解决方案1:将字典类型更改为<string, decimal>

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

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

通过覆盖十进制序列化程序,可以得到预期的结果。

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

解决方案2:将字典类型更改为<string, double>

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

预期结果的结果:{ "decimal" : 3.503, "int" : 45 }

解决方案3:使用自定义序列化程序

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

答案 1 :(得分:0)

作为另一种选择,可以覆盖对象序列化器

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

仍然不适用于Hashtables。 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));