2016-08-18 3 views
3

У меня возникли проблемы с преобразованием класса в документ BSON.Пользовательский BSON Key Value Serializer

У меня есть Custom1 и Custom2, которые должны вести себя немного иначе. Как создать собственный сериализатор, который «разворачивает» KeyValuePair, чтобы он генерировал ожидаемый результат (см. Ниже)? Вы можете увидеть пример кода ниже вместе с ожидаемым результатом.

Кроме того, я использую библиотеку Mongo BSON для сериализации объекта.

public class UserData 
{ 
    public UserData() 
    { 
     Id = 100; 
     Name = "Superuser"; 
     Custom1 = new KeyValuePair<string, double>("HelloWorld1", 1); 
     Custom2 = new KeyValuePair<string, double>("HelloWorld2", 2); 
    } 

    public int Id { get; set; } 
    public string Name { get; set; } 
    public KeyValuePair<string, double> Custom1 { get; set; } 
    public KeyValuePair<string, double> Custom2 { get; set; } 
} 

Выполнить тестовый код:

var userdata = new UserData(); 
var doc = userdata.ToBsonDocument(); 

Текущий результат:

{ 
    "Id": 100, 
    "Name": "Superuser", 
    "Custom1": { 
        "Key": "HelloWorld1", 
        "Value": 1 
       }, 
    "Custom2": { 
        "Key": "HelloWorld2", 
        "Value": 2 
       } 
} 

Ожидаемый результат:

{ 
    "Id": 100, 
    "Name": "Superuser", 
    "HelloWorld1": 1, 
    "HelloWorld2": 2 
} 
+0

Просьба опубликовать результат, который вы получаете также – Newton

+0

@Newton: Добавлен текущий пример результата. – SOK

ответ

1

Для такого сложного случая сериализации вам необходимо реализовать собственный преобразователь IBsonSerializer для вашего класса.

Вот рабочий пример:

public class UserDataSerializer : SerializerBase<UserData> 
{ 
    public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, UserData value) 
    { 
     context.Writer.WriteStartDocument(); 
     context.Writer.WriteName("Id"); 
     context.Writer.WriteInt32(value.Id); 
     context.Writer.WriteName("Name"); 
     context.Writer.WriteString(value.Name); 

     WriteKeyValue(context.Writer, value.Custom1); 
     WriteKeyValue(context.Writer, value.Custom2); 

     context.Writer.WriteEndDocument(); 
    } 

    private void WriteKeyValue(IBsonWriter writer, KeyValuePair<string, double> kv) 
    { 
     writer.WriteName(kv.Key); 
     writer.WriteDouble(kv.Value); 
    } 

    public override UserData Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) 
    { 
     //TODO: implement data deserialization using context.Reader 
     throw new NotImplementedException(); 
    } 
} 

Чтобы сделать его работу также необходимо зарегистрировать наш UserDataSerializer каким-то образом. IBsonSerializationProvider - это самый простой способ достичь этого.

public class UserDataSerializationProvider : IBsonSerializationProvider 
{ 
    public IBsonSerializer GetSerializer(Type type) 
    { 
     if (type == typeof(UserData)) return new UserDataSerializer(); 
     return null; 
    } 
} 

И, наконец, мы можем использовать его.

//register our serialization provider 
BsonSerializer.RegisterSerializationProvider(new UserDataSerializationProvider()); 

var userdata = new UserData(); 
var doc = userdata.ToBsonDocument(); 

Вот результат:

{ "Id" : 100, "Name" : "Superuser", "HelloWorld1" : 1.0, "HelloWorld2" : 2.0 } 

Вы также можете реализовать UserDataSerializer.Deserialize() метод для того, чтобы обеспечить обратную конвертацию. Это можно сделать так же, используя context.Reader.

Дополнительную информацию о процессе пользовательской сериализации можно найти here.

+0

@SOK Пробовали ли вы пример? Есть ли у вас дополнительные вопросы? – kreig

0

Если вы используете драйвер версии 2.0 или выше, вы можете попробуйте сделать свой класс в Dynam icObject, как это:

public class UserData : DynamicObject 
{ 
public UserData() 
    { 
    Id = 100; 
    Name = "Superuser"; 
    Custom1 = new KeyValuePair<string, double>("HelloWorld1", 1); 
    Custom2 = new KeyValuePair<string, double>("HelloWorld2", 2); 
    } 

public id Id { get; set; } 
public string Name { get; set; } 
public KeyValuePair<string, double> Custom1 { get; set; } 
public KeyValuePair<string, double> Custom2 { get; set; } 

public override bool TryGetMember(
     GetMemberBinder binder, out object result) 
    { 
     string name = binder.Name; 
     result = null; 
     if(name.Equals(Custom1.Key)) 
      result = Custom1.Value; 
     else if(name.Equals(Custom2.Key)) 
      result = Custom2.Value; 
     return result != null; 
    } 

    public override bool TrySetMember(
     SetMemberBinder binder, object value) 
    {   
     string name = binder.Name; 
     if(name.Equals(Custom1.Key)) 
     Custom1.Value = value; 
     else if(name.Equals(Custom2.Key)) 
     Custom2.Value = value; 
     return name.Equals(Custom1.Key) || 
      name.Equals(Custom2.Key); 
    } 
    } 

Согласно документации по выпуску, то вы должны быть в состоянии сделать это:

var userdata = new UserData(); var doc = ((dynamic) userdata).ToBsonDocument();

И получить ожидаемый формат.

+0

TryGetMember & TrySetMember являются переопределениями по умолчанию getter и seters? – Newton

+0

@ Милтон: Спасибо за ваш ответ. Я действительно хочу избежать размышлений. Количество значений «Custom» не будет меняться во время выполнения, а «KeyValuePair» всегда будет иметь «Key» и «Value». Я полагал, что может быть создан какой-то пользовательский сериализатор, который просто «сглаживает» «KeyValuePair» '. (Я знаю, что «.ToBsonDocument» использует отражение, но кэширует отображение). – SOK

Смежные вопросы