如何使用 BsonClassMap 更改 MongoDB C# Driver class 反序列化的方式?
How can I use BsonClassMap to change the way a MongoDB C# Driver class is deserialized?
由于我应用到我的变更流的过滤器(在 SO: 中讨论),我得到了一个 BsonDocument 而不是 ChangeStreamDocument 对象。此 BsonDocument 与 ChangeStreamDocument 的唯一不同之处在于它包含一个名为 "tmpfields".
的额外元素
在我的场景中,我仍然需要文档中的 ResumeToken 和其他元素,所以我想将此 BsonDocument 转换为 ChangeStreamDocument 对象。我的第一次尝试是使用 BsonSerializer.Deserialize<ChangeStreamDocument<BsonDocument>>( doc)
,其中 doc 是我返回的 BsonDocument。但是,因为它有额外的 tmpfields 元素,所以这是不允许的。
我尝试注册 BsonClassMap,因为 ChangeStreamDocument class 是 C# 驱动程序的一部分,我无法将 [BsonIgnoreExtraElements] 属性添加到 class,但我没有成功:
BsonClassMap.RegisterClassMap<ChangeStreamDocument<BsonDocument>>(cm =>
{
cm.AutoMap();
cm.SetIgnoreExtraElements(true);
});
虽然 AutoMap()
没有工作,但我得到了一个关于 "no matching creator found" 的异常。我尝试 cm.MapCreator(...)
,但也没有成功。我调用了 AutoMap()
调用(只留下 SetIgnoreExtraElements 行)并得到关于它无法匹配属性(_id 等)的错误。所以我为每个属性尝试了像 cm.MapProperty(c => c.DocumentKey).SetElementName("documentKey")
这样的行,但是当我使用 Deserialize()
方法时它们从未被设置 - 它们被保留为空。
目前,我已恢复使用 doc["field"].AsXYZ
方法从 BsonDocument 中获取我需要的值,但我想学习更好的方法。
使用 RegisterClassMap 是正确的方法吗?如果是这样,我错过了什么?
I couldn't add the [BsonIgnoreExtraElements] attribute to the class
如果你只是想忽略多余的字段。您可以只添加一个额外的聚合管道 $project
来删除该字段。
例如
var options = new ChangeStreamOptions { FullDocument = ChangeStreamFullDocumentOption.UpdateLookup };
var addFields = new BsonDocument { { "$addFields", new BsonDocument { { "tmpfields", new BsonDocument { { "$objectToArray", "$updateDescription.updatedFields" } } } } } };
var match = new BsonDocument { { "$match", new BsonDocument { { "tmpfields.k", new BsonDocument { { "$nin", new BsonArray{"a", "b"} } } } } } };
// Remove the unwanted field.
var project = new BsonDocument { {"$project", new BsonDocument { {"tmpfields", 0 } } } };
var pipeline = new[] { addFields, match, project };
var cursor = collection.Watch<ChangeStreamDocument<BsonDocument>>(pipeline, options);
var enumerator = cursor.ToEnumerable().GetEnumerator();
while(enumerator.MoveNext())
{
ChangeStreamDocument<BsonDocument> doc = enumerator.Current;
Console.WriteLine(doc.DocumentKey);
}
由于我应用到我的变更流的过滤器(在 SO:
在我的场景中,我仍然需要文档中的 ResumeToken 和其他元素,所以我想将此 BsonDocument 转换为 ChangeStreamDocument 对象。我的第一次尝试是使用 BsonSerializer.Deserialize<ChangeStreamDocument<BsonDocument>>( doc)
,其中 doc 是我返回的 BsonDocument。但是,因为它有额外的 tmpfields 元素,所以这是不允许的。
我尝试注册 BsonClassMap,因为 ChangeStreamDocument class 是 C# 驱动程序的一部分,我无法将 [BsonIgnoreExtraElements] 属性添加到 class,但我没有成功:
BsonClassMap.RegisterClassMap<ChangeStreamDocument<BsonDocument>>(cm =>
{
cm.AutoMap();
cm.SetIgnoreExtraElements(true);
});
虽然 AutoMap()
没有工作,但我得到了一个关于 "no matching creator found" 的异常。我尝试 cm.MapCreator(...)
,但也没有成功。我调用了 AutoMap()
调用(只留下 SetIgnoreExtraElements 行)并得到关于它无法匹配属性(_id 等)的错误。所以我为每个属性尝试了像 cm.MapProperty(c => c.DocumentKey).SetElementName("documentKey")
这样的行,但是当我使用 Deserialize()
方法时它们从未被设置 - 它们被保留为空。
目前,我已恢复使用 doc["field"].AsXYZ
方法从 BsonDocument 中获取我需要的值,但我想学习更好的方法。
使用 RegisterClassMap 是正确的方法吗?如果是这样,我错过了什么?
I couldn't add the [BsonIgnoreExtraElements] attribute to the class
如果你只是想忽略多余的字段。您可以只添加一个额外的聚合管道 $project
来删除该字段。
例如
var options = new ChangeStreamOptions { FullDocument = ChangeStreamFullDocumentOption.UpdateLookup };
var addFields = new BsonDocument { { "$addFields", new BsonDocument { { "tmpfields", new BsonDocument { { "$objectToArray", "$updateDescription.updatedFields" } } } } } };
var match = new BsonDocument { { "$match", new BsonDocument { { "tmpfields.k", new BsonDocument { { "$nin", new BsonArray{"a", "b"} } } } } } };
// Remove the unwanted field.
var project = new BsonDocument { {"$project", new BsonDocument { {"tmpfields", 0 } } } };
var pipeline = new[] { addFields, match, project };
var cursor = collection.Watch<ChangeStreamDocument<BsonDocument>>(pipeline, options);
var enumerator = cursor.ToEnumerable().GetEnumerator();
while(enumerator.MoveNext())
{
ChangeStreamDocument<BsonDocument> doc = enumerator.Current;
Console.WriteLine(doc.DocumentKey);
}