MongoDB - 覆盖C#基元类型的默认Serializer

时间:2012-08-30 16:53:17

标签: mongodb-.net-driver

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

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

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

我还可以看到代表双胞胎代表Int64是可能的,但这是演员而不是转换。我需要在两个序列化方向上进行转换和转换。

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

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

3 个答案:

答案 0 :(得分:20)

你绝对可以做到这一点,你只需要正确的时机。当驱动程序启动时,没有注册序列化程序。当它需要一个序列化程序时,它会在字典中查找它跟踪它所知道的序列化程序(即已经注册的序列化程序)。只有它在字典中找不到它才会开始计算出去哪一个(包括调用序列化提供者),如果找到它就注册它。

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

答案 1 :(得分:2)

您还可以使用自己的序列化提供程序告诉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());

答案 2 :(得分:0)

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

就个人而言,我打开一张票 - 如果需要或需要快速解决方法,我会将DoubleSerializer子类化,实现新行为,然后使用Reflection将其注入MongoDB.Bson.Serialization.Serializers.DoubleSerializer.__instance或{ {1}}。