自定义BSON键值序列化器

时间:2016-08-18 06:04:31

标签: c# mongodb bson

我在尝试将类转换为BSON文档时遇到了一些麻烦。

我的Custom1Custom2应该有所不同。如何创建一个“展开”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
}

2 个答案:

答案 0 :(得分:2)

对于这种复杂的序列化案例,您必须为您的班级实现自定义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();
    }
}

为了使它工作,你还需要以某种方式注册我们的UserDataSerializerIBsonSerializationProvider是实现这一目标的最简单方法。

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

答案 1 :(得分:0)

如果您使用的是2.0或更高版本的驱动程序,您可以尝试将您的类变为DynamicObject,如下所示:

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

获得预期的格式。