BsonSerializationException序列化字典&LT时,日期时间,T>到BSON字典、日期、时间、序列化

2023-09-03 02:39:32 作者:语海

我最近搬到新MongoDB的C#的驱动程序V2.0 从德precated V1.9 。

现在,当我序列化,有一本字典我有时碰上一类以下 BsonSerializationException

  

MongoDB.Bson.BsonSerializationException:当使用DictionaryRe presentation.Document键值必须序列化为字符串

下面是一个最小的重现:

 类仓鼠
{
    公众的ObjectId ID为{获得;私定; }
    公共字典<日期时间,INT>字典{获得;私定; }
    公仓鼠()
    {
        的id = ObjectId.GenerateNewId();
        字典=新字典<日期时间,INT>();
        字典[DateTime.UtcNow] = 0;
    }
}
 
python 元组 字典 列表 序列化与反序列化

 静态无效的主要()
{
    Console.WriteLine(新仓鼠()的toJSON());
}
 

解决方案

现在的问题是,新的驱动程序序列化的词典作为文件默认情况下。

MongoDB的C#的驱动器有3种方式序列化词典:文件 ArrayOfArrays &放大器; ArrayOfDocuments (more对,在文档)。当它系列化作为一个文件的字典键是具有一定的局限性的BSON元件的名称(例如,如该错误表明,它们必须被序列化为字符串)。

在这种情况下,字典的键是的DateTime ■哪些不序列化为字符串,但日期如此,我们需要选择另一个 DictionaryRe presentation

要改变这种特定属性,我们可以使用 BsonDictionaryOptions 属性使用不同的 DictionaryRe presentation :

  [BsonDictionaryOptions(DictionaryRe presentation.ArrayOfArrays)
公共字典<日期时间,INT>字典{获得;私定; }
 

不过,我们需要独自做每一个有问题的部件上。若要将此 DictionaryRe presentation 所有相关的成员,我们可以实现迈上了一个新的约定:

 类DictionaryRe presentationConvention:ConventionBase,IMemberMapConvention
{
    私人只读DictionaryRe presentation _dictionaryRe presentation;
    公共DictionaryRe presentationConvention(DictionaryRe presentation dictionaryRe presentation)
    {
        _dictionaryRe presentation = dictionaryRe presentation;
    }
    公共无效申请(BsonMemberMap memberMap)
    {
        memberMap.SetSerializer(ConfigureSerializer(memberMap.GetSerializer()));
    }
    私人IBsonSerializer ConfigureSerializer(IBsonSerializer串行)
    {
        VAR dictionaryRe presentationConfigurable =串作为IDictionaryRe presentationConfigurable;
        如果(dictionaryRe presentationConfigurable!= NULL)
        {
            串行= dictionaryRe presentationConfigurable.WithDictionaryRe presentation(_dictionaryRe presentation);
        }

        VAR childSerializerConfigurable =串行为IChildSerializerConfigurable;
        返回childSerializerConfigurable == NULL
            ?串行
            : childSerializerConfigurable.WithChildSerializer(ConfigureSerializer(childSerializerConfigurable.ChildSerializer));
    }
}
 

这对我们登记如下:

  ConventionRegistry.Register(
    DictionaryRe presentationConvention
    新ConventionPack {新DictionaryRe presentationConvention(DictionaryRe presentation.ArrayOfArrays)},
    _ =>真正);
 

I've recently moved to the new MongoDB C# driver v2.0 from the deprecated v1.9.

Now, when I serialize a class that has a dictionary I sometimes run into the following BsonSerializationException:

MongoDB.Bson.BsonSerializationException: When using DictionaryRepresentation.Document key values must serialize as strings.

Here's a minimal reproduce:

class Hamster
{
    public ObjectId Id { get; private set; }
    public Dictionary<DateTime,int> Dictionary { get; private set; }
    public Hamster()
    {
        Id = ObjectId.GenerateNewId();
        Dictionary = new Dictionary<DateTime, int>();
        Dictionary[DateTime.UtcNow] = 0;
    }
}

static void Main()
{
    Console.WriteLine(new Hamster().ToJson());
}

解决方案

The problem is that the new driver serializes dictionaries as a document by default.

The MongoDB C# driver has 3 ways to serialize a dictionary: Document, ArrayOfArrays & ArrayOfDocuments (more on that in the docs). When it serializes as a document the dictionary keys are the names of the BSON element which has some limitations (for example, as the error suggests, they must be serialized as strings).

In this case, the dictionary's keys are DateTimes which aren't serialized as strings, but as Dates so we need to choose another DictionaryRepresentation.

To change the serialization of this specific property we can use the BsonDictionaryOptions attribute with a different DictionaryRepresentation:

[BsonDictionaryOptions(DictionaryRepresentation.ArrayOfArrays)]
public Dictionary<DateTime, int> Dictionary { get; private set; }

However, we need to do that on every problematic member individually. To apply this DictionaryRepresentation to all the relevant members we can implement a a new convention:

class DictionaryRepresentationConvention : ConventionBase, IMemberMapConvention
{
    private readonly DictionaryRepresentation _dictionaryRepresentation;
    public DictionaryRepresentationConvention(DictionaryRepresentation dictionaryRepresentation)
    {
        _dictionaryRepresentation = dictionaryRepresentation;
    }
    public void Apply(BsonMemberMap memberMap)
    {
        memberMap.SetSerializer(ConfigureSerializer(memberMap.GetSerializer()));
    }
    private IBsonSerializer ConfigureSerializer(IBsonSerializer serializer)
    {
        var dictionaryRepresentationConfigurable = serializer as IDictionaryRepresentationConfigurable;
        if (dictionaryRepresentationConfigurable != null)
        {
            serializer = dictionaryRepresentationConfigurable.WithDictionaryRepresentation(_dictionaryRepresentation);
        }

        var childSerializerConfigurable = serializer as IChildSerializerConfigurable;
        return childSerializerConfigurable == null
            ? serializer
            : childSerializerConfigurable.WithChildSerializer(ConfigureSerializer(childSerializerConfigurable.ChildSerializer));
    }
} 

Which we register as follows:

ConventionRegistry.Register(
    "DictionaryRepresentationConvention",
    new ConventionPack {new DictionaryRepresentationConvention(DictionaryRepresentation.ArrayOfArrays)},
    _ => true);