MongoDB-переопределить сериализатор по умолчанию для примитивного типа C#

Я хотел бы изменить представление двойников C# на округленный Int64 со сдвигом четырех десятичных знаков в стеке драйвера сериализации C# для MongoDB. Другими словами, store (Double)29.99 as (Int64)299900

Я хотел бы, чтобы это было прозрачно для моего приложения. Я посмотрел на пользовательские сериализаторы, но я не хочу переопределять все а затем включите тип с резервным вариантом по умолчанию, так как это немного грязно.

Я вижу, что RegisterSerializer () не позволит мне добавить один для существующего типа, и что BsonDefaultSerializationProvider имеет статический список примитивных сериализаторов, и он помечен как внутренний с частными членами, поэтому я не могу легко подкласс.

Я также вижу, что можно представить Int64 для двойников, но это приведение не преобразование. Мне нужно, по существу, приведение и преобразование в обоих направлениях сериализации.

Я хотел бы просто дать сериализатор по умолчанию пользовательский сериализатор для переопределения одного из его собственных, но это означало бы грязный хак.

Я пропустил действительно простой способ?

3 ответов


вы определенно можете это сделать, вам просто нужно правильно выбрать время. При запуске драйвера сериализаторы не регистрируются. Когда ему нужен сериализатор, он ищет его в словаре, где он отслеживает сериализаторы, о которых он знает (т. е. те, которые были зарегистрированы). Только он не может найти его в словаре, он начинает выяснять, где его получить (включая вызов поставщиков сериализации), и если он находит его, он регистрирует его.

в ограничение в 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);

вы также можете использовать свой собственный поставщик сериализации, чтобы сообщить 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());

Я просмотрел последнюю итерацию кода драйвера и проверил, есть ли какой-то бэкдор для установки пользовательских сериализаторов. Я боюсь, что нет; вы должны открыть проблему в трекере ошибок проекта, если вы думаете, что это нужно рассмотреть для будущих итераций драйвера (https://jira.mongodb.org/).

лично я бы открыл билет - и если требуется быстрое обходное решение, я бы подкласс DoubleSerializer, реализовать новое поведение, а затем использовать отражение, чтобы ввести его в MongoDB.Bson.Serialization.Serializers.DoubleSerializer.__instance или MongoDB.Bson.Serialization.BsonDefaultSerializationProvider.__serializers.