简体   繁体   中英

How can I use BsonClassMap to change the way a MongoDB C# Driver class is deserialized?

Due to the filter I'm applying to my changestream (discussed at SO: How do you filter updates to specific fields from ChangeStream in MongoDB ), I am getting a BsonDocument back instead of a ChangeStreamDocument object. The only thing different about this BsonDocument from a ChangeStreamDocument is that it contains an extra element called "tmpfields".

In my scenario, I still need the ResumeToken and other elements in the document, so I'd like to convert this BsonDocument to a ChangeStreamDocument object. My first attempt was to use BsonSerializer.Deserialize<ChangeStreamDocument<BsonDocument>>( doc) where doc was the BsonDocument I got back. However, since it had the extra tmpfields element, this isn't allowed.

I attempted to register a BsonClassMap since the ChangeStreamDocument class is part of the C# driver and I couldn't add the [BsonIgnoreExtraElements] attribute to the class, but I wasn't successful:

BsonClassMap.RegisterClassMap<ChangeStreamDocument<BsonDocument>>(cm =>
{
    cm.AutoMap();
    cm.SetIgnoreExtraElements(true);
});

The AutoMap() didn't work though and I got an exception about "no matching creator found". I tried to cm.MapCreator(...) , but wasn't succesffuly there either. I took the AutoMap() call out (only leaving the SetIgnoreExtraElements line) and got errors about it not being able to match the properties (_id, etc). So I tried lines like cm.MapProperty(c => c.DocumentKey).SetElementName("documentKey") for each of the properties, but they were never set when I used the Deserialize() method - they were left as null.

For now, I've reverted to using doc["field"].AsXYZ method to get the values that I need from the BsonDocument, but I'd like to learn a better way to do this.

Is using the RegisterClassMap the correct approach? If so, what did I miss?

I couldn't add the [BsonIgnoreExtraElements] attribute to the class

If you just want to ignore the extra field. You could just add an extra aggregation pipeline $project to remove the field.

For example

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

 }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM