9

我想将 C# Doubles 的表示更改为四舍五入的 Int64,并在 MongoDB 的序列化 C# 驱动程序堆栈中移动四位小数。换句话说,将 (Double)29.99 存储为 (Int64)299900

我希望这对我的应用程序是透明的。我已经查看了自定义序列化程序,但我不想覆盖所有内容,然后打开 Type 并回退到默认值,因为这有点混乱。

我可以看到 RegisterSerializer() 不会让我为现有类型添加一个,并且 BsonDefaultSerializationProvider 有一个原始序列化器的静态列表,它被标记为内部的私有成员,所以我不能轻易地继承。

我还可以看到,可以将 RepresentAs Int64 用于 Doubles,但这是转换而不是转换。我基本上需要在两个序列化方向上进行强制转换和转换。

我希望我可以给默认序列化器一个自定义序列化器来覆盖它自己的序列化器,但这意味着一个肮脏的黑客攻击。

我错过了一个非常简单的方法吗?

4

3 回答 3

24

你绝对可以做到这一点,你只需要把握好时机。当驱动程序启动时,没有注册序列化程序。当它需要一个序列化器时,它会在字典中查找它,它会跟踪它知道的序列化器(即那些已经注册的序列化器)。只有它无法在字典中找到一个,它才会开始找出从哪里获得一个(包括调用序列化提供程序),如果找到一个,它就会注册它。

RegisterSerializer 存在限制,因此您无法替换已使用的现有序列化程序。但这并不意味着如果你足够早地注册,你就不能注册自己的。

但是,请记住,注册序列化程序是一项全局操作,因此如果您为 double 注册自定义序列化程序,它将用于所有双精度数,这可能会导致意外结果!

无论如何,您可以像这样编写自定义序列化程序:

public class CustomDoubleSerializer : BsonBaseSerializer
{
    public override object Deserialize(BsonReader bsonReader, Type nominalType, Type actualType, IBsonSerializationOptions options)
    {
        var rep = bsonReader.ReadInt64();
        return rep / 100.0;
    }

    public override void Serialize(BsonWriter bsonWriter, Type nominalType, object value, IBsonSerializationOptions options)
    {
        var rep = (long)((double)value * 100);
        bsonWriter.WriteInt64(rep);
    }
}

并像这样注册它:

BsonSerializer.RegisterSerializer(typeof(double), new CustomDoubleSerializer());

您可以使用以下类对其进行测试:

public class C
{
    public int Id;
    public double X;
}

这个代码:

BsonSerializer.RegisterSerializer(typeof(double), new CustomDoubleSerializer());

var c = new C { Id = 1, X = 29.99 };
var json = c.ToJson();
Console.WriteLine(json);

var r = BsonSerializer.Deserialize<C>(json);
Console.WriteLine(r.X);
于 2012-09-28T14:54:54.677 回答
5

您还可以使用自己的序列化提供程序来告诉 Mongo 为某些类型使用哪个序列化程序,我最终这样做是为了减轻在尝试覆盖现有序列化程序时提到的一些时序问题。这是一个序列化提供程序的示例,它覆盖了如何序列化小数:

public class CustomSerializationProvider : IBsonSerializationProvider
{
    public IBsonSerializer GetSerializer(Type type)
    {
        if (type == typeof(decimal)) return new DecimalSerializer(BsonType.Decimal128);

        return null; // falls back to Mongo defaults
    }
}

如果您null从自定义序列化提供程序返回,它将回退到使用 Mongo 的默认序列化提供程序。

一旦你写好了你的提供者,你只需要注册它:

BsonSerializer.RegisterSerializationProvider(new CustomSerializationProvider());
于 2017-09-28T13:45:04.130 回答
0

我查看了驱动程序代码的最新迭代,并检查是否有某种后门来设置自定义序列化程序。恐怕没有;如果您认为需要在驱动程序的未来迭代中查看该问题(https://jira.mongodb.org/),您应该在项目的错误跟踪器中打开一个问题。

就个人而言,我会开一张票——如果需要或需要快速解决方法,我会将 DoubleSerializer 子类化,实现新行为,然后使用反射将其注入到MongoDB.Bson.Serialization.Serializers.DoubleSerializer.__instanceorMongoDB.Bson.Serialization.BsonDefaultSerializationProvider.__serializers中。

于 2012-09-28T08:12:08.560 回答