我最近搬到新MongoDB的C#的驱动程序V2.0 从德precated V1.9 。
现在,当我序列化,有一本字典我有时碰上一类以下 BsonSerializationException
:
MongoDB.Bson.BsonSerializationException:当使用DictionaryRe presentation.Document键值必须序列化为字符串
下面是一个最小的重现:
类仓鼠
{
公众的ObjectId ID为{获得;私定; }
公共字典<日期时间,INT>字典{获得;私定; }
公仓鼠()
{
的id = ObjectId.GenerateNewId();
字典=新字典<日期时间,INT>();
字典[DateTime.UtcNow] = 0;
}
}
静态无效的主要()
{
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 DateTime
s which aren't serialized as strings, but as Date
s 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);